Search code examples
parsingsyntax-errorbisonjison

Why are my syntax errors in Jison not being "propagated"?


This is the code that I have:

%lex
%options flex

%{
// Used to store the parsed data
if (!('regions' in yy)) {
    yy.regions = {
        settings: {},
        tables: [],
        relationships: []
    };
}
%}

text                [a-zA-Z][a-zA-Z0-9]*

%%

\n\s*               return 'NEWLINE';
[^\S\n]+            ; // ignore whitespace other than newlines
"."                 return '.';
","                 return ',';
"-"                 return '-';
"="                 return '=';
"=>"                return '=>';
"<="                return '<=';
"["                 return '[';
"settings]"         return 'SETTINGS';
"tables]"           return 'TABLES';
"relationships]"    return 'RELATIONSHIPS';
"]"                 return ']';
{text}              return 'TEXT';
<<EOF>>             return 'EOF';

/lex

%left ','

%start source 

%%

source
    : content EOF
        { 
            console.log(yy.regions); 
            console.log("\n" + JSON.stringify(yy.regions)); 
            return yy.regions; 
        }
    | NEWLINE content EOF
        { 
            console.log(yy.regions); 
            console.log("\n" + JSON.stringify(yy.regions)); 
            return yy.regions; 
        }
    | NEWLINE EOF
    | EOF
    ;

content
    : '[' section content
    | '[' section
    ;

section
    : SETTINGS NEWLINE settings_content 
    | TABLES NEWLINE tables_content
    | RELATIONSHIPS NEWLINE relationships_content
    ; 

settings_content
    : settings_line NEWLINE settings_content
    | settings_line NEWLINE
    | settings_line
    ;

settings_line
    : text '=' text
        { yy.regions.settings[$1] = $3; }
    ;

tables_content
    : tables_line NEWLINE tables_content
    | tables_line NEWLINE
    | tables_line 
    ;

tables_line
    : table_name
        { yy.regions.tables.push({ name: $table_name, fields: [] }); }
    | field_list
        {
            var tableCount = yy.regions.tables.length;
            var tableIndex = tableCount - 1;
            yy.regions.tables[tableIndex].fields.push($field_list);
         }
    ;

table_name
    : '-' text
        { $$ = $text; }
    ;

field_list
    : text
        { $$=[]; $$.push($text); }
    | field_list ',' text
        { $field_list.push($text); $$ = $field_list; }
    ;

relationships_content
    : relationships_line NEWLINE relationships_content
    | relationships_line NEWLINE
    | relationships_line 
    ; 

relationships_line
    : relationship_key '=>' relationship_key 
        { 
            yy.regions.relationships.push({
                pkTable: $1,
                fkTable: $3
            }); 
        }
    | relationship_key '<=' relationship_key
        {
            yy.regions.relationships.push({
                pkTable: $3,
                fkTable: $1
            }); 
        }
    ;

relationship_key
    : text '.' text
        { $$ = { name: $1, field: $3 };  }
    | text
        { $$ = { name: $1 }; }
    ;

text
    : TEXT
        { $$ = $TEXT; }
    ;

It's used to parse this kind of code:

[settings]
DefaultFieldType = string

[tables]
-table1
id, int, PK
username, string, NULL
password, string

-table2
id, int, PK
itemName, string
itemCount, int

[relationships]
table1 => table2
foo.test => bar.test2

Into this kind of JSON:

{ settings: { DefaultFieldType: 'string' },
  tables:
   [ { name: 'table1', fields: [Object] },
     { name: 'table2', fields: [Object] } ],
  relationships:
   [ { pkTable: [Object], fkTable: [Object] },
     { pkTable: [Object], fkTable: [Object] } ] }

However I don't get syntax error. When I go to Jison demo and try to parse 5*PI 3^2, I get the following error:

Parse error on line 1:
5*PI 3^2
-----^
Expecting 'EOF', '+', '-', '*', '/', '^', ')', got 'NUMBER'

which is expected. But when I change the last line of the code which I wish to parse from:

foo.test => bar.test2

to something like

foo.test => a bar.test2

I get the following error:

throw new _parseError(str, hash);
      ^
TypeError: Function.prototype.toString is not generic

I traced this to the generated parser code which looks like this:

if (hash.recoverable) {
    this.trace(str);
} else {
    function _parseError (msg, hash) {
        this.message = msg;
        this.hash = hash;
    }
    _parseError.prototype = Error;

    throw new _parseError(str, hash);
}

So this leads me to believe that there is something wrong in how I structured my code and how I handled parsing but I have no idea what that might be.

It seems like it might have something to do with error recovery. If that is correct, how is that supposed to be used? Am I supposed to add the 'error' rule upwards to every element all the way to the source root?


Solution

  • Your grammar seems to work as expected in the Jison demo page, at least with the browser I'm using (Firefox 46.0.1). From the amount of activity in the git repository around the code that you cite, I suspect that the version of jison you are using has one of the bugs:

    I think the jison version on the demo page is older, not newer, so if grabbing the current code from github doesn't work, you could try using an older version.