Squirrel を久しぶりに使う

今の開発には大変疲弊しており気分転換に別の事をやることにしました. 割と重要なのが自作パッチ当てツールです.これは15年ぐらい前にテキトーに作ったものを使っていて、気に入らない部分がたくさんあるけど致命的な理由がなくなんとかなっていたのですが、今回作り直すことにしました.

Squirrel は10年ぐらい前にあった Lua ブームのなか、わたしは anago で採用しましてかなり強力で満足してます. また私が最後に C で組み込む側のプログラムを使ったのが5年前らしいです. 公式の website は 2016年3月以降は更新がないみたいでちょっと寂しく、別の組み込み型スクリプト言語も探している途中です. 将来の開発のためにも mruby 系が気になるのですが調査に時間がかかりそうなので手っ取り早く Squirrel でやりたいことを作りました.

Squirrel Standard Libraries ではテキストファイルが簡単に読み込めない

今回の目的はアセンブラで生成される Motorola S record を読み込んで ROM にパッチを当てるというものです. S record の読み込みはどの言語で作ってもいいのですが、今回は Squirrel で書くことにしました.

それで件名のテキストファイルの話になります. リファレンスを読んでいたのですが、標準ライブラリは正規表現はあるのにテキストファイル読み込みがみあたらず、回りくどくない代替の方法もなぜかありませんでした. 仕方ないので標準ライブラリをいじって fgets() をいれました....

diff -r /e/squirrel3/include/sqstdio.h ./squirrel3/include/sqstdio.h
14a15,17
>     virtual SQInteger Gets(SQChar *str, SQInteger size){
> 		return -1;
> 	}

diff -r /e/squirrel3/sqstdlib/sqstdio.cpp ./squirrel3/sqstdlib/sqstdio.cpp
41a42,47
> SQInteger sqstd_gets(SQChar *str, SQInteger size, SQFILE file)
> {
> 	char *r = fgets(str, size, (FILE *)file);
>     return r == NULL ? -1 : 1;
> }
> 
87a94,96
>     SQInteger Gets(SQChar *str, SQInteger size){
> 		return sqstd_gets(str, size,_handle);
> 	}
diff -r /e/squirrel3/sqstdlib/sqstdstream.cpp ./squirrel3/sqstdlib/sqstdstream.cpp
201c201,208
< 
---
> SQInteger _stream_gets(HSQUIRRELVM v)
> {
>     SETUP_STREAM(v);
>     char str[0x200];
>     self->Gets(str, sizeof(str));
>     sq_pushstring(v, str, -1);
>     return 1;
> }
243a251
>     _DECL_STREAM_FUNC(gets,1,_SC("xn")),

str[0x200] の文字列の長さにつきましては、私がその場しのぎに入れてることをご留意ください.

S record を読むスクリプトを作る

gets 導入後は正規表現でつまづきましたが手っ取り早く作れましたし、実行速度も速いのでいいと思います.

function mot_analyse(str)
{
	local ex = regexp(@"^S([01235789])([0-9A-Fa-f]{8,80})\n?");
	local t = {result = false, error = "", address = 0, data = []};
	if(ex.match(str) == false){
		t.error = "invalid string";
		return t;
	}
	local r = ex.capture(str);
	local mot_type = str.slice(r[1].begin, r[1].end).tointeger();
	local ar = [];
	for(local i = r[2].begin; i < r[2].end; i+=2){
		ar.push(str.slice(i, i+2).tointeger(0x10));
	}
	local sum = 0;
	foreach(s in ar){
		sum += s;
	}
	if((sum & 0xff) != 0xff){
		t.error = "checksum error";
		return t;
	}
	local data_count = ar[0];
	local address_count = 0;
	local pt = 1;
	switch(mot_type){
	case 0: case 5:
	case 1: case 9: address_count = 2; break;
	case 2: case 8: address_count = 3; break;
	case 3: case 7: address_count = 4; break;
	default: t.error = "unknown type"; return t;
	}
	for(local i = 0; i < address_count; i++){
		t.address = (t.address << 8) | ar[pt];
		pt += 1;
	}
	data_count -= address_count;
	data_count -= 1; //checksum
	t.data = ar.slice(pt, pt+data_count);
	t.result = true;
	return t;
}

正規表現は末尾の $ がなくても文字列の最後までチェックしているみたいでした. 仕方ないので \n? をいれてマッチしています. それと () の文字列を取り出す capture が他の言語とちょっと違ったので理解するのに時間がかかりました.

他の言語は文字列自体を返してくれるので r で文字列が得られることを期待していたのですが、r.begin, r[].end を利用し slice で文字列の最初から最後までを取り出せということでした....

Squirrel の将来性は...?

自分だけが開発に使い、パソコンで利用し、他人が作った C ソースやライブラリを組み込んで、スクリプトでテストしまくるという用途では最高です. わたしの将来の用途は安価なワンチップマイコンでの UI の作成に使いたいのですが C++ を使ってる時点で OS なしの低機能な環境ではたぶんだめかなと思いました. いくらか重複しますが気になる点を列挙いたします.

  • website の更新が止まっている
  • インターネットを調べても使っている人が少ないのか参考になる情報が5年前と対して変わってない
  • fgets 相当が標準になく、テキストファイル読み込みがやりづらい
  • 配列,文字列の切り出しが [begin...end] 形式で [begin, length] 形式がない
  • printf がなく print(format()) と書くのが面倒

なかなか難しいところです.