Permalink
Browse files

init

  • Loading branch information...
0 parents commit 265c2b0656c61e1a09700a1f484863472b80836d @zaach committed Aug 30, 2012
Showing with 1,044 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +13 −0 Makefile
  3. +218 −0 README.md
  4. +46 −0 bnf.l
  5. +180 −0 bnf.y
  6. +46 −0 ebnf-parser.js
  7. +153 −0 ebnf-transform.js
  8. +23 −0 package.json
  9. +9 −0 tests/all-tests.js
  10. +91 −0 tests/bnf.js
  11. +153 −0 tests/bnf_parse.js
  12. +72 −0 tests/ebnf.js
  13. +38 −0 tests/ebnf_parse.js
@@ -0,0 +1,2 @@
+parser.js
+node_modules/
@@ -0,0 +1,13 @@
+
+all: install build test
+
+install:
+ npm install
+
+build:
+ node ./node_modules/.bin/jison bnf.y bnf.l
+ mv bnf.js parser.js
+
+test:
+ node tests/all-tests.js
+
218 README.md
@@ -0,0 +1,218 @@
+# ebnf-parser
+
+A parser for BNF and EBNF grammars used by jison.
+
+## install
+
+ npm install bnf-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
46 bnf.l
@@ -0,0 +1,46 @@
+%x action code
+%s bnf ebnf
+
+%%
+
+<bnf,ebnf>"%%" this.begin('code');return '%%';
+
+<ebnf>"(" return '('
+<ebnf>")" return ')'
+<ebnf>"*" return '*'
+<ebnf>"?" return '?'
+<ebnf>"+" return '+'
+
+\s+ /* skip whitespace */
+"//".* /* skip comment */
+"/*"[^*]*"*" return yy.lexComment(this);
+[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 '|';
+"%%" this.begin(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';
+"%lex"[\w\W]*?"/lex" return 'LEX_BLOCK';
+"%"[a-zA-Z]+[^\n]* /* ignore unrecognized decl */
+"<"[a-zA-Z]*">" /* ignore type */
+"{{"[\w\W]*?"}}" yytext = yytext.substr(2, yyleng-4); return 'ACTION';
+"%{"(.|\n)*?"%}" yytext = yytext.substr(2, yytext.length-4);return 'ACTION';
+"{" yy.depth=0; this.begin('action'); return '{';
+"->".* yytext = yytext.substr(2, yyleng-2); return 'ARROW_ACTION';
+. /* ignore bad characters */
+<*><<EOF>> return 'EOF';
+
+<action>[^{}]+ return 'ACTION_BODY';
+<action>"{" yy.depth++; return '{';
+<action>"}" yy.depth==0? this.begin(ebnf ? 'ebnf' : 'bnf') : yy.depth--; return '}';
+
+<code>(.|\n)+ return 'CODE';
+
+%%
Oops, something went wrong.

0 comments on commit 265c2b0

Please sign in to comment.