Issue #142: @ruby/prism 移行ノート #57
takaokouji
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Issue #142: @ruby/prism 移行ノート
概要
スモウルビーの Ruby → ブロック変換パーサーを、Opal + whitequark/parser から
@ruby/prismJavaScript バインディングに移行した。背景・動機
opal.min.js+opal-parser.min.jsで ~3MB)、メンテナンスコストが高い@ruby/prism) が提供されている移行方針
packages/scratch-gui/src/lib/ruby-parser.jsを Prism の薄いラッパーに置き換えpackages/scratch-gui/src/lib/prism-parser.jsを新規作成(WASM ローダー)packages/scratch-gui/src/lib/ruby-to-blocks-converter/を Visitor パターンで再実装主な変更ファイル
src/lib/prism-parser.jsparse()を提供src/lib/ruby-parser.jssrc/lib/ruby-to-blocks-converter/index.jsprocess()を async に変更、Prism parse を使用src/lib/ruby-to-blocks-converter/ast-handlers/core.jsvisitProgramNode,visitStatementsNodeなどsrc/lib/ruby-to-blocks-converter/ast-handlers/expressions.jsvisitCallNodeなど全式ノードsrc/lib/ruby-to-blocks-converter/ast-handlers/control-flow.jsvisitIfNode,visitUnlessNode,visitDefNodeなどsrc/lib/ruby-to-blocks-converter/ast-handlers/assignments.jswebpack.config.jsasset/inlineで埋め込み、Opal 関連を削除package.jsonOpal/whitequark → Prism の AST ノード名対応表
send(node type string)CallNodeargs/ argument listArgumentsNode(.arguments_)argumentsを避けてarguments_lvasgnLocalVariableWriteNodeivasgnInstanceVariableWriteNodegvasgnGlobalVariableWriteNodelvarLocalVariableReadNodeivarInstanceVariableReadNode.nameは@foo形式)gvarGlobalVariableReadNode.nameは$foo形式)op-asgnLocalVariableOperatorWriteNode等+=等の複合代入ifIfNodeunlessUnlessNodecontrol_if_elseに変換blockBlockNode(.body,.parameters)strStringNode(.unescaped.value)symSymbolNode(.unescaped.value)intIntegerNode(.value)floatFloatNode(.value)true/falseTrueNode/FalseNodenilNilNodeselfSelfNodeconstConstantReadNode(.name)constwith scopeConstantPathNode(.name,.parent)hashHashNode(.elements→AssocNode配列)KeywordHashNodefoo(secs: 5)等)arrayArrayNode(.elements)begin/kwbeginBeginNode/StatementsNodedefDefNode(.name,.parameters,.body)returnReturnNode(.arguments_)range(inclusive)RangeNode(.isExcludeEnd() === false)1..10range(exclusive)RangeNode(.isExcludeEnd() === true)1...10移行で問題となった箇所
1.
arguments_プロパティ名Prism JS バインディングでは
argumentsが JavaScript 予約語であるため、ArgumentsNodeには.arguments_でアクセスする。2. WASM のロード方法
prism.wasmのロード戦略はビルド種別によって異なる:file://)/ 開発サーバーasset/inline→data:URLatob()デコード →compile+instantiateBUILD_TYPE=dist-html、https://smalruby.app)asset/resource→https:URLWebAssembly.instantiateStreaming(fetch(url))asset/inlineは prism.wasm を Base64 でバンドルに埋め込む。file://ではfetch()が CORS エラーになるため必要。asset/resourceは別ファイルとして配信。production ではfetch()が使えるのでinstantiateStreaming()でストリーミングコンパイル可能(バンドルサイズ削減・ブラウザキャッシュも効く)。3. InstanceVariableReadNode の
.nameに@が含まれるPrism では
@fooの.nameは"@foo"(@を含む)。variable-utils.jsの_lookupOrCreateVariableOrListで先頭文字チェックで対応済み:4. SymbolNode のキー(HashNode)
Prism の
HashNodeではキーがSymbolNodeとして渡される。Primitiveクラスの_hashCacheでsym:keyName形式の文字列キーに変換してキャッシュ:5. UnlessNode のブランチ順序
Prism の
UnlessNodeは{ predicate, statements (then), elseClause }の構造。Scratch の
control_if_elseは{ CONDITION, SUBSTACK (then), SUBSTACK2 (else) }の順。unless cond; A; else; B; endは「condが偽のときA、真のときB」なので、branches を入れ替えて
control_if_else(cond, branch1=B, branch2=A)に変換する:6. CallNode の receiver が null の場合
Prism では
say("Hello!")のようにレシーバーなしのメソッド呼び出しの.receiverはnull。_getReceiverName(null)→'sprite'(またはステージの場合'stage')として処理する。7. DefNode の parameters
Prism の
DefNode.parametersはParametersNode(Opal では引数リストが直接配列だった)。visitParametersNodeでrequireds配列を返すことで対応:今後の不具合対応のポイント
visitXxxNode)がindex.jsの visit から呼ばれるか確認node.constructor.nameで型を判定する箇所が多い。Prism の AST が変わった場合に影響ありarguments_のアクセス: 全ての引数アクセスはnode.arguments_.arguments_の形式(ダブルアンダースコア)sym:keyName形式でキャッシュされるvisitUnlessNodeは branches を入れ替える特殊な変換が必要テスト構成
test/unit/lib/ruby-to-blocks-converter/以下(Prism WASM のモックあり)test/integration/ruby-tab/以下(実際のブラウザで変換をテスト)test/unit/lib/setup-prism.js(Node.js 環境での WASM ロード)削除したもの
packages/scratch-gui/opal/ディレクトリ全体packages/scratch-gui/scripts/make-setup-opal.jspackages/scratch-gui/static/javascripts/setup-opal.jspackages/scratch-gui/test/setup-opal.jspackage.json:setup:opalスクリプト、prebuildフック、opal-loader依存関係、Jest のopal/opal-parserエイリアスwebpack.config.js: opal エイリアス、.rbファイルの opal-loader ルールsrc/playground/index.ejs:setup-opal.jsの<script>タグsrc/lib/log-suppression.js: Opal 警告メッセージの抑制Beta Was this translation helpful? Give feedback.
All reactions