<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); }); });