「8kB ROM, 1kB RAM」の極小JavaScriptエンジン「MINIScript」を使ってみます。
まず “Clone or download” から “Download ZIP” でソースコードをダウンロードします。
内容はこんな具合です。”readme.c” が使い方となる説明になっていて、ひとまず
$ gcc -m32 miniscript.c mslib.c readme.c && ./a.out < sample.js
とすればビルドしてサンプルJSを動かすことができます。
今回は自分で一から使ってみましょう。
まずは空っぽのCファイル、ここでは “myproduct.c” を用意します。
int main(){ MyStack stack; MyScope scope; Thread thread; Stack_(&stack.base, SIZE_STACK); Scope_(&scope.base, SIZE_SCOPE); Thread_(&thread, src, &stack.base, &scope.base); Thread_run(&thread); //[*1] Thread__(&thread); Scope__(&scope.base); Stack__(&stack.base); }
これが最低限、肝となるコードです。その中でも特に [*1] がスクリプトを実際に実行する箇所です。命名規則上、コンストラクタはクラス名の後ろにアンダーバー “_” が1つ、デストラクタはアンダーバーが2つです。Stackオブジェクト stack とScopeオブジェクト scope を作り、それらを用いて Threadオブジェクト thread をコンストラクト、最後に Threadクラスのメンバ関数 Thread_run を呼べば完了です。JavaScriptのソースコードは thread コンストラクト時に渡しておきます。thread,scope,stackの各オブジェクトは使い終わったらデストラクトしておきます。
#define SIZE_STACK 0x40 typedef struct{ Stack base; Var vars[SIZE_STACK]; } MyStack;
stackオブジェクトはMyStackクラスですが、このようにStackクラスから派生させてください。派生元のStackクラスは実メモリを持たない状態で宣言されています。SIZE_STACKで好みの深さのStackとして派生させてください。
#define SIZE_SCOPE 0x40 typedef struct{ Scope base; VarMap nvars[SIZE_SCOPE]; } MyScope;
scopeオブジェクトについてもstack同様です。scopeオブジェクトは実行中の変数や関数の名前を保持することに使われます。JavaScriptソースコード中で多くの名前が登場する場合にはSIZE_SCOPEを多めに設定してください。
#define MS_SIZE_POOLEDOBJ 0x04 // object member max #define MS_SIZE_POOLEDARR 0x20 // array length max #define MS_SIZE_VARNAME 0x8 // var name length max #define MS_SIZE_PROPNAME 0x8 // property name length max #include "miniscript.h"
あとは “miniscript.h” を #include すればいいのですが、このヘッダーファイルは設定すべき値が4つあり、#include より前に上記のように適当な値を設定してください。
const char src[] = "var calc = function(p1, p2){ return p1*p2; };" "var a = calc(3, 6);" ;
今回のJavaScriptソースは試しにこのようにしてみましょう。”a” が 18 になるはずです。
printf("%d\n", Stack_tip(&stack.base, -1)->num);
スクリプト実行後、stackの最後尾を調べれば 変数a の値が入っているはずですのでこのようにしてprintfして見てみましょう。
#include <stdio.h> #define MS_SIZE_POOLEDOBJ 0x04 // object member max #define MS_SIZE_POOLEDARR 0x20 // array length max #define MS_SIZE_VARNAME 0x8 // var name length max #define MS_SIZE_PROPNAME 0x8 // property name length max #include "miniscript.h" #define SIZE_STACK 0x40 typedef struct{ Stack base; Var vars[SIZE_STACK]; } MyStack; #define SIZE_SCOPE 0x40 typedef struct{ Scope base; VarMap nvars[SIZE_SCOPE]; } MyScope; const char src[] = "var calc = function(p1, p2){ return p1*p2; };" "var a = calc(3, 6);" ; Var Stack_OVERFLOW; int main(){ MyStack stack; MyScope scope; Thread thread; Stack_(&stack.base, SIZE_STACK); Scope_(&scope.base, SIZE_SCOPE); Thread_(&thread, src, &stack.base, &scope.base); Thread_run(&thread); printf("%d\n", Stack_tip(&stack.base, -1)->num); Thread__(&thread); Scope__(&scope.base); Stack__(&stack.base); }
こちらが今回の “myproduct.c” の全貌です。保存したら,
$ gcc -m32 miniscript.c myproduct.c && ./a.out
でビルド&実行してみましょう。
18
と表示されましたか?
今回はJavaScriptの配列やオブジェクトを利用しませんでしたが、それらを使うこともできます。詳しくは付属の readme.c をご覧ください。
「MINIScript」は NXP LPC1114 用 組み込みOS「IchigoLatte」のために作られたJSエンジンです。容量的な制約が多い中、少しでもフットプリントの小さいエンジンを目指してヒープは使わずメモリはCスタックやグローバルに確保します。ちなみにIchigoLatteでは libc も自前実装してあります。
ECMAScriptのサブセットとして作っていて機能はまだまだ少ないですが、今後はJSON表記に対応したり、いろいろ加えたいなと思っています。みなさんの楽しいご意見、お待ちしています!
.
Pingback: 極小JavaScriptエンジン「MINIScript」、GitHubに登場!フットプリントはROM 8kB、RAM 1kB – about yrm
Pingback: A tiny JavaScript engine “MINIScript” appears on GitHub! It’s need only 8kB for ROM and 1kB for RAM. – about yrm