LoginSignup
0
0

More than 3 years have passed since last update.

Go言語でJavaScriptを使ってオリジナルなコマンドシェルを作る(gojaのちょっとした改造)

Posted at

概要

このシリーズの最後の投稿です。
JavaScriptエンジンであるgojaは仕様がECMAScript 5.1なのでProxyなどはありません。そこで未定義のプロパティの参照があったらオブジェクトの特定のメソッドを呼び出すように改造します。
用途は「Go言語でJavaScriptを使ってオリジナルなコマンドシェルを作る」のAppendix のsqlite3の例の最後の方に書きました。

gojaの改造

改造しないときのデータベースアクセスの例

データベースアクセスの疑似オブジェクトの例
次のようなクラスをdb.jsとして作成します。

db.js
DB=function(name) {
    this.name = name
    //Go言語のsql.Openを呼び出し、*sql.DBをラップしたオブジェクトを返す
}

DB.prototype.exec=function(sql) {
    //sql.DB.Execを実行
    print(sql+"; を実行しました")
}
DB.prototype.query=function(sql) {
    print(sql+"; を実行しました")
    //sql.DB.Queryを実行し
    //*sql.Rowsをラップしたオブジェクトを返す
    return [1,2,3,4]
}

DB.prototype.table = function(name) {
  return new TABLE(this, name)
}

TABLE = function(db, name) {
  this.db = db
  this.name = name
}
TABLE.prototype.findAll = function() {
   return this.db.query("select * from "+this.name)
}

実行

> load("db.js")
undefined
> db=new DB("database")
{"name":"database"}
> db.exec("create table sample(a integer, b varchar)")
create table sample(a integer, b varchar); を実行しました
undefined
> db.query("select * from sample")
select * from sample; を実行しました
[1,2,3,4]
> s=db.table("sample")
{"db":{"name":"database"},"name":"sample"}
> s.findAll()
select * from sample; を実行しました
[1,2,3,4]
>

s=db.table("sample")をMongoDBのmongoシェルのようにs=db.sampleまたはdb.sample.findAll()のように使いたいがProxyなどがないので実現できない。当然、次のようにエラーになります。

> db.sample.findAll()
TypeError: Cannot read property 'findAll' of undefined at console:1:1(3)
>

そこで、gojaを改造します。

gojaのobject.goを改造

object.gofunc getPropStr()があります。現在のソースだと120行目です。次のようなコードです。

object.go
func (o *baseObject) getPropStr(name string) Value {
    if val := o.getOwnProp(name); val != nil {
        return val
    }
    if o.prototype != nil {
        return o.prototype.self.getPropStr(name)
    }
    return nil
}

これを次のように改造します。未定義のプロパティを参照したとき__missing__が定義されていたら、未定義のプロパティ名を引数にして呼び出します。

object.go
func (o *baseObject) getPropStr(name string) Value {
    if val := o.getOwnProp(name); val != nil {
        return val
    }
    if o.prototype == nil {
        return nil
    }
    if val := o.prototype.self.getPropStr(name); val != nil {
        return val
    }
    getProp := o.getOwnProp("__missing__")
    if getProp == nil {
        return nil
    }
    if getFunc, ok := AssertFunction(getProp); ok {
        nameVal := o.val.runtime.ToValue(name)
        if ret, err := getFunc(o.val, nameVal); err == nil {
            return ret
        } else {
            o.val.runtime.Interrupt(err)
            return nil
        }
    }
    return nil
}

db.jsDB__missing__を追加します

db.js
DB=function(name) {
    this.name = name
    //Go言語のsql.Openを呼び出し、*sql.DBをラップしたオブジェクトを返す
    this.__missing__ = function(prop) {
      return this.table(prop)
    }
}

実行

go run main.go
> load("db.js")
undefined
> db=new DB("database")
{"name":"database"}
> db.sample.findAll()
select * from sample; を実行しました
[1,2,3,4]
> s=db.sample
{"db":{"name":"database"},"name":"sample"}
> s.findAll()
select * from sample; を実行しました
[1,2,3,4]
>

考えた通りに動作しました。

まとめ

ちょっとした改造でオリジナルなJavaScriptエンジンも作れます。頑張ればProxyクラスも作れそうです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0