Open11

可能な限りimportの実行を遅延したgoのインタプリタが作りたい

podhmopodhmo

minigo

goのインタプリタが欲しい

理由

  • go/packagesが遅い
  • packageに対する操作(メソッド)が持てない

機能

  • 遅くて全然良い
  • channelとgoroutineの実装は要らない
  • importを可能な限り遅延したい
  • vm的なものをdumpしたい
  • エントリーポイントを変えたい(main)
  • docstringに対してmarkerを付けたい

more

  • replみたいなものが欲しい
  • 速くしたい
  • FFI?
podhmopodhmo

利用方法が2種類あるかもしれない?

  • 既存のgoのコードを実行する
  • go generateで実行する変形として利用する

あと可能ならFFIも欲しい

podhmopodhmo

既存のコードの実行

例えばwebAPIのルーティングの記述のコードを実行してOpenAPI docを生成する

📝 コードのコメントをメタデータとして取り出したい。実行時に利用したい。

//minigo:description 何かここに
router.Get("/xxx/{userId}", handler.GetUser)

実際のアプリケーションコードでもあり定義のコードでもある。

疑問

  • go runではだめなの?
  • 何でインタプリタにしたいの?
  • mainを実行するなら結果は同じじゃないの?
  • goでvalidなコードを要求するなら結局reflectと闘うことになるの?
podhmopodhmo

go generateで実行する変形として利用する

パッケージをオブジェクトととして利用したい。関数を取り出してあれこれしたりenumの生成みたいなHaskellで言うderivingとかrustのderiveとかdartのマクロ的なことがしたい(rustやlispのマクロみたいなものはなくて良い)

こちらはインタプリタでしか実行しないコード。

(しかしgoとしてはvalidであってほしいしgopls経由でdiagnosticはでてきてほしい)

📝 マーカーコメントを付けた関数のbodyを書き換えたい

https://doc.rust-lang.org/reference/attributes/derive.html

https://dart.dev/language/macros#use-cases

疑問

  • go generateではだめなの?
  • go/astを直接使うのではだめなの?
  • パッケージオブジェクトにはどうやってアクセスするの?
  • swaggoみたいなレベルではだめなの?

https://github.com/swaggo/swag

少しずれてるが参考になるもの

コードからコードを出力する例

https://github.com/cheekybits/genny

jsx的なもの(パーサーは自作。jsのprismaのファイルと似たような感じかも?)

https://github.com/a-h/templ

なんか手軽にgoのコードを出力したい

https://github.com/dave/jennifer

goに拘らず独自言語で定義したら(あるいはprotobufだとか)

https://github.com/microsoft/typespec

go/astでやると辛いのはこちらの方が良いかも

https://github.com/dave/dst

podhmopodhmo

go generateではなぜだめなの?

co-location的な話かも?

TODO: 真面目に書く

実行自体はgo generateでされるかも。
go runが嫌な理由は?

podhmopodhmo

調査: FFIをgoの plugin で

[!NOTE]
優先度は低い

可能ならwrapperとかも書きたくない…

どうしてほしいのか?

TODO: 書く

structなどを返したい

https://gist.github.com/podhmo/b472609ee211f520c519fb4eda7d3cb7#file-main-go

mainとpluginの方でそれぞれimportしてあげると大丈夫みたい

⚠️ go:linknameみたいな機構を使って無理やりその場で定義した同じレイアウトのstructの値として利用するとかはできないみたい (plugin b)

initの回数

実験。以下のような別々のpluginから共通して依存してるxのinitは2回呼ばれてしまうんだろうか?

試してみたらinit()は一度きりになってくれるみたい

gistを更新した。 https://gist.github.com/podhmo/b472609ee211f520c519fb4eda7d3cb7?permalink_comment_id=5150713#gistcomment-5150713

package x

import "fmt"

var V = 10

func init() {
	V++
	fmt.Println("on init, V is", V)
}

というxをplugin_aでもplugin_bでもimportしても一度きりみたい

TODO: goのビルドキャッシュとかオブジェクトレイアウトとか調べる(buildmodeとか)。

podhmopodhmo

調査: ファイルの読み込みを減らしたい

[!NOTE]
優先度は低い

  • structの定義場所が分からない

キャッシュする?シンボルとファイルの一覧を持っておければ減らせるかも?

実態を保持していれば関数から取り出すことは出来るかも?

(ビルドキャッシュ的なバイナリからとりだすこともできるかも?)

go.modで記述されてる依存は変更がない事が多いかも?

podhmopodhmo

importを可能な限り遅延したいとは?

dynamic import的な感じに一部だけを読み込みたい。

TODO: 真面目に書く