<head> <meta charset="UTF-8"> <title>JavaScriptでプログラミング - SGF Parser 0.1</title> <link type="text/css" rel="stylesheet" href="lib/jasmine-1.3.1/jasmine.css"> <link type="text/css" rel="stylesheet" href="css/spec.css"> <link type="text/css" rel="stylesheet" href="css/shCoreEclipse.css"> <link type="text/css" rel="stylesheet" href="css/shThemeEclipse.css"> <script type="text/javascript" src="js/XRegExp.js"></script> <script type="text/javascript" src="js/shCore.js"></script> <script type="text/javascript" src="js/shBrushJScript.js"></script> <script type="text/javascript" src="js/shBrushXml.js"></script> <script type="text/javascript" src="js/shBrushCss.js"></script> <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script> <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script> <!-- include source files here... --> <script type="text/javascript" src="js/generator03/source01.js"></script> <script type="text/javascript" src="js/generator03/lex03.js"></script> <script type="text/javascript" src="js/generator03/generator03.js"></script> <!-- include spec files here... --> <script type="text/javascript" src="js/generator03/sourcespec01.js"></script> <script type="text/javascript" src="js/generator03/lexspec03.js"></script> <script type="text/javascript" src="js/generator03/generatorspec03.js"></script>
generator02.js にあった構文の定義を EBNFParser に渡す処理を、 HTML ファイルの <head> タグの後半に持ってきました。 これで色々な構文を試すときも js ファイルのほうを変更しなくて済みます。
最後の部分は Jasmine によるテスト結果を HTML 上で展開するレポーターと呼ばれる部分です。前回から変更ありません。
<script type="text/javascript">
// define SGF syntax
var gapFree = ["PropIdent", "Number", "Real", "Text", "SimpleText"];
var sgfSyntax = "Collection = GameTree, {GameTree}.\n";
sgfSyntax += "GameTree = '(', Sequence, {GameTree}, ')'.\n";
sgfSyntax += "Sequence = Node, {Node}.\n";
sgfSyntax += "Property = PropIdent, PropValue, {PropValue}.\n";
sgfSyntax += "PropIdent = upper, {upper}.\n";
sgfSyntax += "PropValue = '[', CValueType, ']'.\n";
sgfSyntax += "CValueType = ValueType | Compose.\n";
sgfSyntax += "ValueType = None | Number | Real | Double | Color | SimpleText | Text | Move.\n";
sgfSyntax += "None = [sp].\n";
sgfSyntax += "Number = ['+' | '-'], digit, {digit}.\n";
sgfSyntax += "Real = Number, ['.', digit, {digit}].\n";
sgfSyntax += "Double = '1' | '2'.\n";
sgfSyntax += "Color = 'B' | 'W'.\n";
sgfSyntax += "Move = (lower | upper), (lower | upper).\n";
sgfSyntax += "Compose = ValueType, ':', ValueType.\n";
var ebnf = new EBNFParser(sgfSyntax, gapFree, "SGF", "0.1");
SyntaxHighlighter.all();
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
また、今回は <body> タグの実行結果のところに直接スクリプトを書きました。
これは結果に対して SyntaxHighlighter を使用するためです。
実行結果
以下は実際に使用している SGF の構文です。 <script type="text/javascript"> // get element from HTML and set text (code) var html = '<pre class="code">\n'; html += sgfSyntax; html += '</pre>\n'; document.writeln(html); </script> 上記の SGF の構文の文字列を EBNFParser に渡し、SGFParser のコードを以下の通り生成しました。 字下げは Aptana Studio 等のツールで簡単にできるので行っていません。 <script type="text/javascript"> // get element from HTML and set text (code) var html = "<pre class=\"brush: js; class-name: 'list'\">\n"; html += ebnf.syntax(); html += "</pre>\n"; document.writeln(html); </script>
describe("Source 仕様 0.1", function() {
var exp = "1+2=";
var src;
beforeEach(function() {
src = new Source(exp); // Lexer and Parser Source
});
it("eod() は false を返す", function() {
expect(src.eod()).toBeFalsy();
});
describe("先頭でpush()しptr++したとき、", function() {
beforeEach(function() {
src.rewind();
src.push();
src.ptr++;
});
it("pop(true)後、ptrは 0 を返す", function() {
var p = src.ptr;
src.pop(true);
expect(src.ptr).toEqual(0);
});
it("pop(false)後、ptrは 1 を返す", function() {
var p = src.ptr;
src.pop(false);
expect(src.ptr).toEqual(1);
});
});
});
describe("Lex 仕様 0.3", function() {
var exp = "1+2=";
var la;
beforeEach(function() {
la = new Lex(exp); // Lexical Analiser
});
it("eod() は false を返す", function() {
expect(la.eod()).toBeFalsy();
});
it("digit() は '1' を返す", function() {
expect(la.digit()).toEqual('1');
});
it("upper() は null を返す", function() {
expect(la.upper()).toBe(null);
});
it("lower() は null を返す", function() {
expect(la.lower()).toBe(null);
});
it("sp() は null を返す", function() {
expect(la.sp()).toBeFalsy();
});
it("sq() は null を返す", function() {
expect(la.sq()).toBeFalsy();
});
it("ch('1') は '1' を返す", function() {
expect(la.ch('1')).toEqual('1');
});
it("text('1') は '1' を返す", function() {
expect(la.text('1')).toBe('1');
});
it("text('1+') は '1+' を返す", function() {
expect(la.text('1+')).toBe('1+');
});
it("text('2') は null を返す", function() {
expect(la.text('2')).toBe(null);
});
describe("3文字読んだ時点で、", function() {
beforeEach(function() {
la.rewind();
la.digit();
la.ch('+');
la.digit();
});
it("eod() は false を返す", function() {
expect(la.eod()).toBe(false);
});
it("digit() は null を返す", function() {
expect(la.digit()).toBe(null);
});
it("upper() は null を返す", function() {
expect(la.upper()).toBe(null);
});
it("lower() は null を返す", function() {
expect(la.lower()).toEqual(null);
});
it("ch('=') は '=' を返す", function() {
expect(la.ch('=')).toBe('=');
});
});
describe("4文字読んだ時点で、", function() {
beforeEach(function() {
la.rewind();
la.digit();
la.ch('+');
la.digit();
la.ch('=');
});
it("eod() は true を返す", function() {
expect(la.eod()).toBeTruthy();
});
});
});
describe("Generator 仕様 0.3", function() {
var g = new EBNFParser("syntax = {line, nl}.", [], "Test", "0.3");
var code = "/**\n";
code += " * @fileOverview TestParser - Test Parser Object\n";
code += " * @version 0.3\n";
code += " * @author EBNFParser written by Nonki Takahashi\n";
code += " */\n";
code += "\n";
code += "/**\n";
code += " * Test Parser Object\n";
code += " * @class Represents Test Parser Object\n";
code += " * @this {TestParser}\n";
code += " * @param {String} src source to parser\n";
code += " * @param {Object} gapFree array of gap free id\n";
code += " * @param {String} name name of the parser\n";
code += " * @param {String} ver version of the parser\n";
code += " * @param {Boolean} ignoreCase true if ignore case (optional)\n";
code += " * @property {String} buf source buffer\n";
code += " * @property {Integer} ptr source buffer pointer\n";
code += " * @property {Object} gapFree array of gap free id\n";
code += " * @property {String} name name of the parser\n";
code += " * @property {String} ver version of the parser\n";
code += " * @property {Boolean} ignoreCase true if ignore case\n";
code += " * @since 0.3\n";
code += " */\n";
code += "TestParser = function(src, gapFree, name, ver, ignoreCase) {\n";
code += "this.gapFree = gapFree;\n";
code += "this.name = name;\n";
code += "this.ver = ver;\n";
code += "if (ignoreCase)\n";
code += "this.ignoreCase = true;\n";
code += "else\n";
code += "this.ignoreCase = false;\n";
code += "// inherit the methods of class Lex\n";
code += "Lex.call(this, src);\n";
code += "};\n";
code += "TestParser.prototype = new Lex();\n";
code += "\n";
code += "/**\n";
code += " * syntax = {line, nl}.\n";
code += " * @return {String} code generated if matched, null if not matched\n";
code += " * @since 0.3\n";
code += " */\n";
code += "TestParser.prototype.syntax = function() {\n";
code += "var match = [];\n";
code += "var save = this.ptr;\n";
code += "var n = -1;\n";
code += "this.sp();\n";
code += "while(match[n]) {\n";
code += "this.sp();\n";
code += "match[++n] = this.line();\n";
code += "if (match[n]) {\n";
code += "this.sp();\n";
code += "match[++n] = this.nl();\n";
code += "}\n";
code += "}\n";
code += "match[++n] = true;\n";
code += "if (!match[n]) {\n";
code += "this.ptr = save;\n";
code += "return null;\n";
code += "}\n";
code += "return this.run(n, match, \"syntax\");\n";
code += "};\n\n";
it("syntax() は " + code + " を返す", function() {
expect(g.syntax()).toEqual(code);
});
});