2005-12-17 SimpleJSLex
■[JavaScript]SimpleJSLex
を見てて、字句解析器生成ツール作ってくれないかなぁと書いてあったので、シンプルな字句解析器ジェネレータを作ってみました。
JavaScriptは正規表現オブジェクト利用すれば結構楽に出来ました。
function generateLexer(tokens, name, r) {
function escape2(str) {
return str.replace(/\n/mg, "\\n")
.replace(/\r/mg, "\\r")
.replace(/\t/mg, "\\t")
.replace(/\"/mg, "\\\"")
}
function escape(str) {
return escape2(str.replace(/\\/mg, "\\\\"))
}
for(var s in name) if (typeof(name[s]) == "object") name[s]=name[s].source; // RegExp->String
var flg;
do {
flg = false;
for(var s in name) {
var str = name[s];
if (/{[a-zA-Z0-9]+}/.test(str)) {
flg = true;
for(var s2 in name) {
str = str.replace(new RegExp("{" + s2 + "}", "g"), name[s2]);
}
name[s] = str;
}
}
} while (flg);
var out = new Array();
for(var i in name) {
out[out.length] = i + ": \"" + escape(name[i]) + "\"";
}
var a2 = new Array();
var f2 = new Array();
for (var str in r) {
var a = r[str];
for (var s2 in name) {
str = str.replace(new RegExp("{" + s2 + "}", "g"), name[s2]);
}
a2[a2.length] = "/^"+escape2(str) +"/";
f2[f2.length] = "function()"+ a;
}
tokens = tokens.split(/\s/);
var o = "var buffer,line,";
if (tokens.length > 0) {
for(var i = 0; i < tokens.length;i++) {
tokens[i] = tokens[i] + "="+(i+1);
}
o += tokens.join(",");
}
o +=
",\nyylexa=[" + a2.join(",") + "],"+
"\nyylexf=[" + f2.join(",") + "];\n"+
"function yylex(){"+
"a:for(;;){"+
"for(var i=0;i<yylexa.length;i++){"+
"var r=yylexa[i],"+
"m=buffer.match(r);"+
"if(m!=null&&m.length>0&&m[0].length>0){"+
"token=m[0];"+
"buffer=buffer.substring(m[0].length, buffer.length);"+
"r=yylexf[i]();"+
"if(r!=null)return r;"+
"continue a;"+
"}"+
"}"+
"return(buffer.length==0)?0:-1;"+
"}"+
"}\n";
return o;
}
使い方
以下の例のように使います。
var lexer = generateLexer(
'IDENTIFIER STRING LPAR RPAR SHARP QUOTE DOT',{
letter: /[\x00-\xff]/,
kanji: /([\x80-\xff]{letter})/,
mark: /[\!\$\%\&\*\+\-\.\/\:\<\=\>\?\@\^\_~]/,
alpha: /[A-Za-z]/,
digit: /[0-9]/,
ws: /[ \t\n]/,
com: /\;/,
cr: /\n/,
escape: /\\/,
escaped: /({escape}{letter})/,
dquote: /\"/,
ident: /({kanji}|{mark}|{alpha}|{digit})*/,
comment: /{com}[^\n]*{cr}/,
string: /{dquote}([^{escape}{dquote}]|{escaped})*{dquote}/
},{
"{cr}": "{line++;}",
"{ws}+": "{}",
"{comment}": "{}",
"\\.": "{return DOT;}",
"{ident}": "{return IDENTIFIER;}",
"{string}": "{return STRING;}",
"\\(": "{return LPAR;}",
"\\)": "{return RPAR;}",
"\\#": "{return SHARP;}",
"\\'": "{return QUOTE;}"
});
document.write(lexer.replace(/</mg,"<").replace(/\n/mg,"<br>\n"));
eval(lexer);
main();
function main() {
line = 1;
buffer = "(a b cdef 1.23333)\n1 2";
var t="";
while((t = yylex())!=0) {
document.write(t+":("+line+")"+token+"<br>");
}
}
generateLexer関数
第1引数は、tokenの名前をスペース区切りで書きます。
第3引数は、アクションの正規表現文字列とアクションを決めます。
generateLexer関数から帰ってくるのはjavascriptのプログラムの文字列ですので、
evalして使ったり、htmlに貼り付けて使ってください。
出来上がったプログラムを実行するには、line変数に1を設定し、buffer変数にプログラムを入れておいて、yylex関数を呼び出します。yylex関数からはトークンの番号が帰ってきて、取得した男トークンはtoken変数に格納されて来ます。
コメントを書く