8kB JavaScriptエンジン「MINIScript」を使ってみよう!Cで書かれたスレッドクラスの使い方やスタッククラスの派生方法を徹底詳説!

「8kB ROM, 1kB RAM」の極小JavaScriptエンジン「MINIScript」を使ってみます。

Screen Shot 2018-08-29 at 10.25.48.png

まず “Clone or download” から “Download ZIP” でソースコードをダウンロードします。

Screen Shot 2018-08-29 at 10.27.53.png

内容はこんな具合です。”readme.c” が使い方となる説明になっていて、ひとまず

$ gcc -m32 miniscript.c mslib.c readme.c && ./a.out < sample.js

とすればビルドしてサンプルJSを動かすことができます。

 

今回は自分で一から使ってみましょう。

Screen Shot 2018-08-29 at 13.38.52.png

まずは空っぽの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表記に対応したり、いろいろ加えたいなと思っています。みなさんの楽しいご意見、お待ちしています!

 

 

 

 

 

 

 

 

.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s