ebnf-parser-master/000775 000000 000000 00000000000 12320042031 014516 5ustar00rootroot000000 000000 ebnf-parser-master/.gitignore000664 000000 000000 00000000074 12320042031 016507 0ustar00rootroot000000 000000 parser.js node_modules/ # Editor bak files *~ *.bak *.orig ebnf-parser-master/.npmignore000664 000000 000000 00000000000 12320042031 016503 0ustar00rootroot000000 000000 ebnf-parser-master/Makefile000664 000000 000000 00000000473 12320042031 016162 0ustar00rootroot000000 000000 all: install build test install: npm install build: node ./node_modules/.bin/jison bnf.y bnf.l mv bnf.js parser.js node ./node_modules/.bin/jison ebnf.y mv ebnf.js transform-parser.js test: node tests/all-tests.js clean: superclean: clean -find . -type d -name 'node_modules' -exec rm -rf "{}" \; ebnf-parser-master/README.md000664 000000 000000 00000010476 12320042031 016005 0ustar00rootroot000000 000000 # ebnf-parser A parser for BNF and EBNF grammars used by jison. ## install npm install ebnf-parser ## build To build the parser yourself, clone the git repo then run: make This will generate `parser.js`, which is required by `ebnf-parser.js`. ## usage The parser translates a string grammar or JSON grammar into a JSON grammar that jison can use (ENBF is transformed into BNF). var ebnfParser = require('ebnf-parser'); // parse a bnf or ebnf string grammar ebnfParser.parse("%start ... %"); // transform an ebnf JSON gramamr ebnfParser.transform({"ebnf": ...}); ## example grammar The parser can parse its own BNF grammar, shown below: %start spec /* grammar for parsing jison grammar files */ %{ var transform = require('./ebnf-transform').transform; var ebnf = false; %} %% spec : declaration_list '%%' grammar optional_end_block EOF {$$ = $1; return extend($$, $3);} | declaration_list '%%' grammar '%%' CODE EOF {$$ = $1; yy.addDeclaration($$,{include:$5}); return extend($$, $3);} ; optional_end_block : | '%%' ; declaration_list : declaration_list declaration {$$ = $1; yy.addDeclaration($$, $2);} | {$$ = {};} ; declaration : START id {$$ = {start: $2};} | LEX_BLOCK {$$ = {lex: $1};} | operator {$$ = {operator: $1};} | ACTION {$$ = {include: $1};} ; operator : associativity token_list {$$ = [$1]; $$.push.apply($$, $2);} ; associativity : LEFT {$$ = 'left';} | RIGHT {$$ = 'right';} | NONASSOC {$$ = 'nonassoc';} ; token_list : token_list symbol {$$ = $1; $$.push($2);} | symbol {$$ = [$1];} ; grammar : production_list {$$ = $1;} ; production_list : production_list production {$$ = $1; if($2[0] in $$) $$[$2[0]] = $$[$2[0]].concat($2[1]); else $$[$2[0]] = $2[1];} | production {$$ = {}; $$[$1[0]] = $1[1];} ; production : id ':' handle_list ';' {$$ = [$1, $3];} ; handle_list : handle_list '|' handle_action {$$ = $1; $$.push($3);} | handle_action {$$ = [$1];} ; handle_action : handle prec action {$$ = [($1.length ? $1.join(' ') : '')]; if($3) $$.push($3); if($2) $$.push($2); if ($$.length === 1) $$ = $$[0]; } ; handle : handle expression_suffix {$$ = $1; $$.push($2)} | {$$ = [];} ; handle_sublist : handle_sublist '|' handle {$$ = $1; $$.push($3.join(' '));} | handle {$$ = [$1.join(' ')];} ; expression_suffix : expression suffix {$$ = $expression + $suffix; } ; expression : ID {$$ = $1; } | STRING {$$ = ebnf ? "'"+$1+"'" : $1; } | '(' handle_sublist ')' {$$ = '(' + $handle_sublist.join(' | ') + ')'; } ; suffix : {$$ = ''} | '*' | '?' | '+' ; prec : PREC symbol {$$ = {prec: $2};} | {$$ = null;} ; symbol : id {$$ = $1;} | STRING {$$ = yytext;} ; id : ID {$$ = yytext;} ; action : '{' action_body '}' {$$ = $2;} | ACTION {$$ = $1;} | ARROW_ACTION {$$ = '$$ ='+$1+';';} | {$$ = '';} ; action_body : {$$ = '';} | ACTION_BODY {$$ = yytext;} | action_body '{' action_body '}' ACTION_BODY {$$ = $1+$2+$3+$4+$5;} | action_body '{' action_body '}' {$$ = $1+$2+$3+$4;} ; %% // transform ebnf to bnf if necessary function extend (json, grammar) { json.bnf = ebnf ? transform(grammar) : grammar; return json; } ## license MIT ebnf-parser-master/bnf.l000664 000000 000000 00000004742 12320042031 015447 0ustar00rootroot000000 000000 id [a-zA-Z][a-zA-Z0-9_-]* %x action code %s bnf ebnf %% "%%" this.pushState('code');return '%%'; "(" return '('; ")" return ')'; "*" return '*'; "?" return '?'; "+" return '+'; \s+ /* skip whitespace */ "//".* /* skip comment */ "/*"(.|\n|\r)*?"*/" /* skip comment */ "["{id}"]" yytext = yytext.substr(1, yyleng-2); return 'ALIAS'; {id} return 'ID'; '"'[^"]+'"' yytext = yytext.substr(1, yyleng-2); return 'STRING'; "'"[^']+"'" yytext = yytext.substr(1, yyleng-2); return 'STRING'; ":" return ':'; ";" return ';'; "|" return '|'; "%%" this.pushState(ebnf ? 'ebnf' : 'bnf'); return '%%'; "%ebnf" if (!yy.options) yy.options = {}; ebnf = yy.options.ebnf = true; "%prec" return 'PREC'; "%start" return 'START'; "%left" return 'LEFT'; "%right" return 'RIGHT'; "%nonassoc" return 'NONASSOC'; "%parse-param" return 'PARSE_PARAM'; "%options" return 'OPTIONS'; "%lex"[\w\W]*?"/lex" return 'LEX_BLOCK'; "%"[a-zA-Z]+[^\r\n]* /* ignore unrecognized decl */ "<"[a-zA-Z]*">" /* ignore type */ "{{"[\w\W]*?"}}" yytext = yytext.substr(2, yyleng-4); return 'ACTION'; "%{"(.|\r|\n)*?"%}" yytext = yytext.substr(2, yytext.length-4); return 'ACTION'; "{" yy.depth = 0; this.pushState('action'); return '{'; "->".* yytext = yytext.substr(2, yyleng-2); return 'ARROW_ACTION'; . /* ignore bad characters */ <*><> return 'EOF'; "/*"(.|\n|\r)*?"*/" return 'ACTION_BODY'; "//".* return 'ACTION_BODY'; "/"[^ /]*?['"{}'][^ ]*?"/" return 'ACTION_BODY'; // regexp with braces or quotes (and no spaces) \"("\\\\"|'\"'|[^"])*\" return 'ACTION_BODY'; "'"("\\\\"|"\'"|[^'])*"'" return 'ACTION_BODY'; [/"'][^{}/"']+ return 'ACTION_BODY'; [^{}/"']+ return 'ACTION_BODY'; "{" yy.depth++; return '{'; "}" if (yy.depth==0) this.begin(ebnf ? 'ebnf' : 'bnf'); else yy.depth--; return '}'; (.|\n|\r)+ return 'CODE'; %% ebnf-parser-master/bnf.y000664 000000 000000 00000007224 12320042031 015462 0ustar00rootroot000000 000000 %start spec /* grammar for parsing jison grammar files */ %{ var transform = require('./ebnf-transform').transform; var ebnf = false; %} %% spec : declaration_list '%%' grammar optional_end_block EOF { $$ = $1; return extend($$, $3); } | declaration_list '%%' grammar '%%' CODE EOF { $$ = $1; yy.addDeclaration($$, { include: $5 }); return extend($$, $3); } ; optional_end_block : | '%%' ; declaration_list : declaration_list declaration {$$ = $1; yy.addDeclaration($$, $2);} | {$$ = {};} ; declaration : START id {$$ = {start: $2};} | LEX_BLOCK {$$ = {lex: $1};} | operator {$$ = {operator: $1};} | ACTION {$$ = {include: $1};} | parse_param {$$ = {parseParam: $1};} | options {$$ = {options: $1};} ; options : OPTIONS token_list {$$ = $2;} ; parse_param : PARSE_PARAM token_list {$$ = $2;} ; operator : associativity token_list {$$ = [$1]; $$.push.apply($$, $2);} ; associativity : LEFT {$$ = 'left';} | RIGHT {$$ = 'right';} | NONASSOC {$$ = 'nonassoc';} ; token_list : token_list symbol {$$ = $1; $$.push($2);} | symbol {$$ = [$1];} ; grammar : production_list {$$ = $1;} ; production_list : production_list production { $$ = $1; if ($2[0] in $$) $$[$2[0]] = $$[$2[0]].concat($2[1]); else $$[$2[0]] = $2[1]; } | production {$$ = {}; $$[$1[0]] = $1[1];} ; production : id ':' handle_list ';' {$$ = [$1, $3];} ; handle_list : handle_list '|' handle_action {$$ = $1; $$.push($3);} | handle_action {$$ = [$1];} ; handle_action : handle prec action { $$ = [($1.length ? $1.join(' ') : '')]; if($3) $$.push($3); if($2) $$.push($2); if ($$.length === 1) $$ = $$[0]; } ; handle : handle expression_suffix {$$ = $1; $$.push($2)} | {$$ = [];} ; handle_sublist : handle_sublist '|' handle {$$ = $1; $$.push($3.join(' '));} | handle {$$ = [$1.join(' ')];} ; expression_suffix : expression suffix ALIAS {$$ = $expression + $suffix + "[" + $ALIAS + "]"; } | expression suffix {$$ = $expression + $suffix; } ; expression : ID {$$ = $1; } | STRING {$$ = ebnf ? "'" + $1 + "'" : $1; } | '(' handle_sublist ')' {$$ = '(' + $handle_sublist.join(' | ') + ')'; } ; suffix : {$$ = ''} | '*' | '?' | '+' ; prec : PREC symbol {$$ = {prec: $2};} | {$$ = null;} ; symbol : id {$$ = $1;} | STRING {$$ = yytext;} ; id : ID {$$ = yytext;} ; action : '{' action_body '}' {$$ = $2;} | ACTION {$$ = $1;} | ARROW_ACTION {$$ = '$$ =' + $1 + ';';} | {$$ = '';} ; action_body : {$$ = '';} | action_comments_body {$$ = $1;} | action_body '{' action_body '}' action_comments_body {$$ = $1 + $2 + $3 + $4 + $5;} | action_body '{' action_body '}' {$$ = $1 + $2 + $3 + $4;} ; action_comments_body : ACTION_BODY { $$ = yytext; } | action_comments_body ACTION_BODY { $$ = $1+$2; } ; %% // transform ebnf to bnf if necessary function extend (json, grammar) { json.bnf = ebnf ? transform(grammar) : grammar; return json; } ebnf-parser-master/ebnf-parser.js000664 000000 000000 00000002334 12320042031 017262 0ustar00rootroot000000 000000 var bnf = require("./parser").parser, ebnf = require("./ebnf-transform"), jisonlex = require("lex-parser"); exports.parse = function parse (grammar) { return bnf.parse(grammar); }; exports.transform = ebnf.transform; // adds a declaration to the grammar bnf.yy.addDeclaration = function (grammar, decl) { if (decl.start) { grammar.start = decl.start; } else if (decl.lex) { grammar.lex = parseLex(decl.lex); } else if (decl.operator) { if (!grammar.operators) grammar.operators = []; grammar.operators.push(decl.operator); } else if (decl.parseParam) { if (!grammar.parseParams) grammar.parseParams = []; grammar.parseParams = grammar.parseParams.concat(decl.parseParam); } else if (decl.include) { if (!grammar.moduleInclude) grammar.moduleInclude = ''; grammar.moduleInclude += decl.include; } else if (decl.options) { if (!grammar.options) grammar.options = {}; for (var i=0; i < decl.options.length; i++) { grammar.options[decl.options[i]] = true; } } }; // parse an embedded lex section var parseLex = function (text) { return jisonlex.parse(text.replace(/(?:^%lex)|(?:\/lex$)/g, '')); }; ebnf-parser-master/ebnf-transform.js000664 000000 000000 00000010007 12320042031 017775 0ustar00rootroot000000 000000 var EBNF = (function(){ var parser = require('./transform-parser.js'); var transformExpression = function(e, opts, emit) { var type = e[0], value = e[1], name = false; if (type === 'xalias') { type = e[1]; value = e[2] name = e[3]; if (type) { e = e.slice(1,2); } else { e = value; type = e[0]; value = e[1]; } } if (type === 'symbol') { var n; if (e[1][0] === '\\') n = e[1][1]; else if (e[1][0] === '\'') n = e[1].substring(1, e[1].length-1); else n = e[1]; emit(n + (name ? "["+name+"]" : "")); } else if (type === "+") { if (!name) { name = opts.production + "_repetition_plus" + opts.repid++; } emit(name); opts = optsForProduction(name, opts.grammar); var list = transformExpressionList([value], opts); opts.grammar[name] = [ [list, "$$ = [$1];"], [ name + " " + list, "$1.push($2);" ] ]; } else if (type === "*") { if (!name) { name = opts.production + "_repetition" + opts.repid++; } emit(name); opts = optsForProduction(name, opts.grammar); opts.grammar[name] = [ ["", "$$ = [];"], [ name + " " + transformExpressionList([value], opts), "$1.push($2);" ] ]; } else if (type ==="?") { if (!name) { name = opts.production + "_option" + opts.optid++; } emit(name); opts = optsForProduction(name, opts.grammar); opts.grammar[name] = [ "", transformExpressionList([value], opts) ]; } else if (type === "()") { if (value.length == 1) { emit(transformExpressionList(value[0], opts)); } else { if (!name) { name = opts.production + "_group" + opts.groupid++; } emit(name); opts = optsForProduction(name, opts.grammar); opts.grammar[name] = value.map(function(handle) { return transformExpressionList(handle, opts); }); } } }; var transformExpressionList = function(list, opts) { return list.reduce (function (tot, e) { transformExpression (e, opts, function (i) { tot.push(i); }); return tot; }, []). join(" "); }; var optsForProduction = function(id, grammar) { return { production: id, repid: 0, groupid: 0, optid: 0, grammar: grammar }; }; var transformProduction = function(id, production, grammar) { var transform_opts = optsForProduction(id, grammar); return production.map(function (handle) { var action = null, opts = null; if (typeof(handle) !== 'string') action = handle[1], opts = handle[2], handle = handle[0]; var expressions = parser.parse(handle); handle = transformExpressionList(expressions, transform_opts); var ret = [handle]; if (action) ret.push(action); if (opts) ret.push(opts); if (ret.length == 1) return ret[0]; else return ret; }); }; var transformGrammar = function(grammar) { Object.keys(grammar).forEach(function(id) { grammar[id] = transformProduction(id, grammar[id], grammar); }); }; return { transform: function (ebnf) { transformGrammar(ebnf); return ebnf; } }; })(); exports.transform = EBNF.transform; ebnf-parser-master/ebnf.y000664 000000 000000 00000002223 12320042031 015621 0ustar00rootroot000000 000000 /* EBNF grammar spec */ %lex id [a-zA-Z][a-zA-Z0-9_-]* %% \s+ /* skip whitespace */ {id} return 'symbol'; "["{id}"]" yytext = yytext.substr(1, yyleng-2); return 'ALIAS'; "'"[^']*"'" return 'symbol'; "." return 'symbol'; bar return 'bar'; "(" return '('; ")" return ')'; "*" return '*'; "?" return '?'; "|" return '|'; "+" return '+'; <> return 'EOF'; /lex %start production %% production : handle EOF { return $handle; } ; handle_list : handle { $$ = [$handle]; } | handle_list '|' handle { $handle_list.push($handle); } ; handle : { $$ = []; } | handle expression_suffix { $handle.push($expression_suffix); } ; expression_suffix : expression suffix ALIAS { $$ = ['xalias', $suffix, $expression, $ALIAS]; } | expression suffix { if ($suffix) $$ = [$suffix, $expression]; else $$ = $expression; } ; expression : symbol { $$ = ['symbol', $symbol]; } | '(' handle_list ')' { $$ = ['()', $handle_list]; } ; suffix : | '*' | '?' | '+' ; ebnf-parser-master/package.json000664 000000 000000 00000001043 12320042031 017002 0ustar00rootroot000000 000000 { "name": "ebnf-parser", "version": "0.1.10", "description": "A parser for BNF and EBNF grammars used by jison", "main": "ebnf-parser.js", "scripts": { "test": "make test" }, "repository": { "type": "git", "url": "https://github.com/zaach/ebnf-parser.git" }, "keywords": [ "bnf", "ebnf", "grammar", "parser", "jison" ], "author": "Zach Carter", "license": "MIT", "devDependencies": { "jison": "git://github.com/zaach/jison.git#ef2647", "lex-parser": "0.1.0", "test": "*" } } ebnf-parser-master/tests/000775 000000 000000 00000000000 12320042031 015660 5ustar00rootroot000000 000000 ebnf-parser-master/tests/all-tests.js000775 000000 000000 00000000404 12320042031 020127 0ustar00rootroot000000 000000 #!/usr/bin/env narwhal //exports.testBNF = require("./bnf"); exports.testBNFParse = require("./bnf_parse"); exports.testEBNF = require("./ebnf"); exports.testEBNFParse = require("./ebnf_parse"); if (require.main === module) require("test").run(exports); ebnf-parser-master/tests/bnf.js000664 000000 000000 00000007076 12320042031 016775 0ustar00rootroot000000 000000 var Jison = require("../setup").Jison, Lexer = require("../setup").Lexer, assert = require("assert"); exports["test BNF parser"] = function () { var grammar = { "lex": { "rules": [ ["\\s+", "/* skip whitespace */"], ["[a-zA-Z][a-zA-Z0-9_-]*", "return 'ID';"], ["\"[^\"]+\"", "yytext = yytext.substr(1, yyleng-2); return 'STRING';"], ["'[^']+'", "yytext = yytext.substr(1, yyleng-2); return 'STRING';"], [":", "return ':';"], [";", "return ';';"], ["\\|", "return '|';"], ["%%", "return '%%';"], ["%prec", "return 'PREC';"], ["%start", "return 'START';"], ["%left", "return 'LEFT';"], ["%right", "return 'RIGHT';"], ["%nonassoc", "return 'NONASSOC';"], ["\\{[^}]*\\}", "yytext = yytext.substr(1, yyleng-2); return 'ACTION';"], [".", "/* ignore bad characters */"], ["$", "return 'EOF';"] ] }, "bnf": { "spec" :[[ "declaration_list %% grammar EOF", "$$ = $1; $$.bnf = $3; return $$;" ]], "declaration_list" :[[ "declaration_list declaration", "$$ = $1; yy.addDeclaration($$, $2);" ], [ "", "$$ = {};" ]], "declaration" :[[ "START id", "$$ = {start: $2};" ], [ "operator", "$$ = {operator: $1};" ]], "operator" :[[ "associativity token_list", "$$ = [$1]; $$.push.apply($$, $2);" ]], "associativity" :[[ "LEFT", "$$ = 'left';" ], [ "RIGHT", "$$ = 'right';" ], [ "NONASSOC", "$$ = 'nonassoc';" ]], "token_list" :[[ "token_list symbol", "$$ = $1; $$.push($2);" ], [ "symbol", "$$ = [$1];" ]], "grammar" :[[ "production_list", "$$ = $1;" ]], "production_list" :[[ "production_list production", "$$ = $1; $$[$2[0]] = $2[1];" ], [ "production", "$$ = {}; $$[$1[0]] = $1[1];" ]], "production" :[[ "id : handle_list ;", "$$ = [$1, $3];" ]], "handle_list" :[[ "handle_list | handle_action", "$$ = $1; $$.push($3);" ], [ "handle_action", "$$ = [$1];" ]], "handle_action" :[[ "handle action prec", "$$ = [($1.length ? $1.join(' ') : '')]; if($2) $$.push($2); if($3) $$.push($3); if ($$.length === 1) $$ = $$[0];" ]], "handle" :[[ "handle symbol", "$$ = $1; $$.push($2)" ], [ "", "$$ = [];" ]], "prec" :[[ "PREC symbol", "$$ = {prec: $2};" ], [ "", "$$ = null;" ]], "symbol" :[[ "id", "$$ = $1;" ], [ "STRING", "$$ = yytext;" ]], "id" :[[ "ID", "$$ = yytext;" ]], "action" :[[ "ACTION", "$$ = yytext;" ], [ "", "$$ = '';" ]] } }; var parser = new Jison.Parser(grammar); parser.yy.addDeclaration = function (grammar, decl) { if (decl.start) { grammar.start = decl.start } if (decl.operator) { if (!grammar.operators) { grammar.operators = []; } grammar.operators.push(decl.operator); } }; var result = parser.parse('%start foo %left "+" "-" %right "*" "/" %nonassoc "=" STUFF %left UMINUS %% foo : bar baz blitz { stuff } %prec GEMINI | bar %prec UMINUS | ;\nbar: { things };\nbaz: | foo ;'); assert.ok(result, "parse bnf production"); }; ebnf-parser-master/tests/bnf_parse.js000664 000000 000000 00000022545 12320042031 020165 0ustar00rootroot000000 000000 var assert = require("assert"), bnf = require("../ebnf-parser"); exports["test basic grammar"] = function () { var grammar = "%% test: foo bar | baz ; hello: world ;"; var expected = {bnf: {test: ["foo bar", "baz"], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test multiple same rule"] = function () { var grammar = "%% test: foo bar | baz ; test: world ;"; var expected = {bnf: {test: ["foo bar", "baz", "world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test classy grammar"] = function () { var grammar = "%%\n\npgm \n: cdl MAIN LBRACE vdl el RBRACE ENDOFFILE \n; cdl \n: c cdl \n| \n;"; var expected = {bnf: {pgm: ["cdl MAIN LBRACE vdl el RBRACE ENDOFFILE"], cdl: ["c cdl", ""]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test advanced grammar"] = function () { var grammar = "%% test: foo bar {action} | baz ; hello: world %prec UMINUS ;extra: foo %prec '-' {action} ;"; var expected = {bnf: {test: [["foo bar", "action" ], "baz"], hello: [[ "world", {prec:"UMINUS"} ]], extra: [[ "foo", "action", {prec: "-"} ]]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test nullable rule"] = function () { var grammar = "%% test: foo bar | ; hello: world ;"; var expected = {bnf: {test: ["foo bar", ""], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test nullable rule with action"] = function () { var grammar = "%% test: foo bar | {action}; hello: world ;"; var expected = {bnf: {test: ["foo bar", [ "", "action" ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test nullable rule with %{ %} delimited action"] = function () { var grammar = "%% test: foo bar | %{action{}%}; hello: world ;"; var expected = {bnf: {test: ["foo bar", [ "", "action{}" ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test nullable rule with {{ }} delimited action"] = function () { var grammar = "%% test: foo bar | {{action{};}}; hello: world ;"; var expected = {bnf: {test: ["foo bar", [ "", "action{};" ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test rule with {{ }} delimited action"] = function () { var grammar = "%% test: foo bar {{ node({}, node({})); }}; hello: world ;"; var expected = {bnf: {test: [["foo bar"," node({}, node({})); " ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test comment"] = function () { var grammar = "/* comment */ %% hello: world ;"; var expected = {bnf: {hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test single line comment"] = function () { var grammar = "//comment \n %% hello: world ;"; var expected = {bnf: {hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parse comment"); }; exports["test comment with nested *"] = function () { var grammar = "/* comment * not done */ %% hello: /* oh hai */ world ;"; var expected = {bnf: {hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test token"] = function () { var grammar = "%token blah\n%% test: foo bar | baz ; hello: world ;"; var expected = {bnf: {test: ["foo bar", "baz"], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test token with type"] = function () { var grammar = "%type blah\n%% test: foo bar | baz ; hello: world ;"; var expected = {bnf: {test: ["foo bar", "baz"], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test embedded lexical block"] = function () { var grammar = "%lex \n%%\n'foo' return 'foo';\n'bar' {return 'bar';}\n'baz' {return 'baz';}\n'world' {return 'world';}\n/lex\ %% test: foo bar | baz ; hello: world ;"; var expected = { lex: { rules: [ ["foo\\b", "return 'foo';"], ["bar\\b", "return 'bar';"], ["baz\\b", "return 'baz';"], ["world\\b", "return 'world';"] ] }, bnf: {test: ["foo bar", "baz"], hello: ["world"]} }; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test balanced braces"] = function () { var grammar = "%% test: foo bar { node({}, node({foo:'bar'})); }; hello: world ;"; var expected = {bnf: {test: [["foo bar"," node({}, node({foo:'bar'})); " ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test brace within a multi-line comment"] = function () { var grammar = "%% test: foo bar { node({}, 3 / 4); /* { */ }; hello: world ;"; var expected = {bnf: {test: [["foo bar"," node({}, 3 / 4); /* { */ " ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test brace within a single-line comment"] = function () { var grammar = "%% test: foo bar { node({}); // {\n }; hello: world ;"; var expected = {bnf: {test: [["foo bar"," node({}); // {\n " ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test brace within a string"] = function () { var grammar = "%% test: foo bar { node({}, 3 / 4, '{'); /* { */ }; hello: world ;"; var expected = {bnf: {test: [["foo bar"," node({}, 3 / 4, '{'); /* { */ " ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test brace within a string with double quotes"] = function () { var grammar = "%% test: foo bar { node({}, 3 / 4, \"{\"); /* { */ }; hello: world ;"; var expected = {bnf: {test: [["foo bar"," node({}, 3 / 4, \"{\"); /* { */ " ]], hello: ["world"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test uneven braces and quotes within regex"] = function () { var grammar = "%% test: foo bar { node({}, 3 / 4, \"{\"); /{'\"/g; 1 / 2; }; hello: world { blah / bah };"; var expected = {bnf: {test: [["foo bar"," node({}, 3 / 4, \"{\"); /{'\"/g; 1 / 2; " ]], hello: [["world", " blah / bah "]]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test code declaration"] = function () { var grammar = "%{var foo = 'bar';%}\n%%hello: world;"; var expected = {bnf: {hello: ["world"]}, moduleInclude: "var foo = 'bar';"}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test remainder code"] = function () { var grammar = "%%hello: world;%%var foo = 'bar';"; var expected = {bnf: {hello: ["world"]}, moduleInclude: "var foo = 'bar';"}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test remainder and declarations code"] = function () { var grammar = "%{test;%}\n%%hello: world;%%var foo = 'bar';"; var expected = {bnf: {hello: ["world"]}, moduleInclude: "test;var foo = 'bar';"}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test expression action"] = function () { var grammar = "%% test: foo bar -> $foo\n;"; var expected = {bnf: {test: [["foo bar","$$ = $foo;"]]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test quote in rule"] = function () { var grammar = "%lex\n%%\n\\' return \"'\"\n/lex\n%% test: foo bar \"'\";"; var expected = {lex: { rules: [ ["'", "return \"'\""] ] }, bnf: {test: ["foo bar '"]}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test windows line endings"] = function () { var grammar = "%{baz\r\n%}%% test: foo bar | {\r\naction;\r\nhi};\r\nhello: world ;%%foo;\r\nbar;"; var expected = {bnf: {test: ["foo bar", [ "", "\r\naction;\r\nhi" ]], hello: ["world"]}, moduleInclude: 'baz\r\nfoo;\r\nbar;'}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test parse params"] = function () { var grammar = "%parse-param first second\n%%hello: world;%%"; var expected = {bnf: {hello: ["world"]}, parseParams: ["first", "second"]}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; exports["test options"] = function () { var grammar = "%options one two\n%%hello: world;%%"; var expected = {bnf: {hello: ["world"]}, options: {one: true, two: true}}; assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); }; ebnf-parser-master/tests/ebnf.js000664 000000 000000 00000010340 12320042031 017126 0ustar00rootroot000000 000000 var assert = require("assert"), ebnf = require("../ebnf-transform"); var Parser = require('jison').Parser; function testParse(top, strings) { return function() { var grammar = { "lex": { "rules": [ ["\\s+", ''], ["[A-Za-z]+", "return 'word';"], [",", "return ',';"], ["$", "return 'EOF';"] ] }, "start": "top", "bnf": ebnf.transform({"top": [top]}) }; strings = (typeof(strings) === 'string' ? [strings] : strings); strings.forEach(function(string) { assert.ok(new Parser(grammar).parse(string)); }); }; } function testBadParse(top, strings) { return function() { var grammar = { "lex": { "rules": [ ["\\s+", ''], ["[A-Za-z]+", "return 'word';"], [",", "return ',';"], ["$", "return 'EOF';"] ] }, "start": "top", "ebnf": {"top": [top]} }; strings = (typeof(strings) === 'string' ? [strings] : strings); strings.forEach(function(string) { assert.throws(function () {new Parser(grammar).parse(string);}) }); }; } function testAlias(top, obj, str) { return function() { var grammar = { "lex": { "rules": [ ["\\s+", ''], ["[A-Za-z]+", "return 'word';"], [",", "return ',';"], ["$", "return 'EOF';"] ] }, "start": "top", "bnf": ebnf.transform({"top": [top]}) }; assert.deepEqual(grammar['bnf'], obj); assert.ok(new Parser(grammar).parse(str)); }; } var tests = { "test idempotent transform": function() { var first = { "nodelist": [["", "$$ = [];"], ["nodelist node", "$1.push($2);"]] }; var second = ebnf.transform(JSON.parse(JSON.stringify(first))); assert.deepEqual(second, first); }, "test repeat (*) on empty string": testParse("word* EOF", ""), "test repeat (*) on single word": testParse("word* EOF", "oneword"), "test repeat (*) on multiple words": testParse("word* EOF", "multiple words"), "test repeat (+) on empty string": testBadParse("word+ EOF", ""), "test repeat (+) on single word": testParse("word+ EOF", "oneword"), "test repeat (+) on multiple words": testParse("word+ EOF", "multiple words"), "test option (?) on empty string": testParse("word? EOF", ""), "test option (?) on single word": testParse("word? EOF", "oneword"), "test group () on simple phrase": testParse("(word word) EOF", "two words"), "test group () with multiple options on first option": testParse("((word word) | word) EOF", "hi there"), "test group () with multiple options on second option": testParse("((word word) | word) EOF", "hi"), "test complex expression ( *, ?, () )": testParse("(word (',' word)*)? EOF ", ["", "hi", "hi, there"]), "test named repeat (*)": testAlias("word*[bob] EOF", { top: [ 'bob EOF' ], bob: [ [ '', '$$ = [];' ], [ 'bob word', '$1.push($2);' ] ] }, "word"), "test named repeat (+)": testAlias("word+[bob] EOF", { top: [ 'bob EOF' ], bob: [ [ 'word', '$$ = [$1];' ], [ 'bob word', '$1.push($2);' ] ] }, "wordy word"), "test named group ()": testAlias("word[alice] (',' word)*[bob] EOF", {"top":["word[alice] bob EOF"],"bob":[["","$$ = [];"],["bob , word","$1.push($2);"]]}, "one, two"), "test named option (?)": testAlias("word[alex] word?[bob] EOF", { top: [ 'word[alex] bob EOF' ], bob: [ '', 'word' ] }, "oneor two"), "test named complex expression (())": testAlias("word[alpha] (word[alex] (word[bob] word[carol] ',')+[david] word ',')*[enoch] EOF", {"top":["word[alpha] enoch EOF"],"david":[["word[bob] word[carol] ,","$$ = [$1];"],["david word[bob] word[carol] ,","$1.push($2);"]], "enoch":[["","$$ = [];"],["enoch word[alex] david word ,","$1.push($2);"]]}, "one two three four, five," ) }; for (var test in tests) { exports[test] = tests[test]; } ebnf-parser-master/tests/ebnf_parse.js000664 000000 000000 00000003162 12320042031 020324 0ustar00rootroot000000 000000 var assert = require("assert"), bnf = require("../ebnf-parser"), ebnf = require("../ebnf-transform"); function testParse(top, strings) { return function() { var expected = { "bnf": ebnf.transform({"top": [top]}) }; var grammar = "%ebnf\n%%\ntop : "+top+";"; assert.deepEqual(bnf.parse(grammar), expected); }; } var tests = { "test idempotent transform": function() { var first = { "nodelist": [["", "$$ = [];"], ["nodelist node", "$1.push($2);"]] }; var second = ebnf.transform(JSON.parse(JSON.stringify(first))); assert.deepEqual(second, first); }, "test repeat (*) on empty string": testParse("word* EOF", ""), "test repeat (*) on single word": testParse("word* EOF", "oneword"), "test repeat (*) on multiple words": testParse("word* EOF", "multiple words"), "test repeat (+) on single word": testParse("word+ EOF", "oneword"), "test repeat (+) on multiple words": testParse("word+ EOF", "multiple words"), "test option (?) on empty string": testParse("word? EOF", ""), "test option (?) on single word": testParse("word? EOF", "oneword"), "test group () on simple phrase": testParse("(word word) EOF", "two words"), "test group () with multiple options on first option": testParse("((word word) | word) EOF", "hi there"), "test group () with multiple options on second option": testParse("((word word) | word) EOF", "hi"), "test complex expression ( *, ?, () )": testParse("(word (',' word)*)? EOF", ["", "hi", "hi, there"]) }; for (var test in tests) { exports[test] = tests[test]; }