diff --git a/lib/compiler.js b/lib/compiler.js index b761fb1..30448c8 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -23,19 +23,10 @@ function nop() {} PEG.buildParser = function(grammar, startRule) { startRule = startRule || "start"; - var ast = PEG.grammarParser.parse(grammar); - - for (var rule in ast) { - ast[rule].checkReferencedRulesExist(ast); - } - if (ast[startRule] === undefined) { - throw new PEG.Grammar.GrammarError("Missing \"" + startRule + "\" rule."); - } - for (var rule in ast) { - ast[rule].checkNoLeftRecursion(ast, []); - } - - return PEG.Compiler.compileParser(ast, startRule); + return PEG.Compiler.compileParser( + PEG.grammarParser.parse(grammar), + startRule + ); }; /* ===== PEG.ArrayUtils ===== */ @@ -462,18 +453,57 @@ PEG.Compiler = { }, /* - * Generates a parser from a specified grammar and start rule. + * Checks made on the grammar AST before compilation. Each check is a function + * that is passed the AST and start rule and does not return anything. If the + * check passes, the function does not do anything special, otherwise it + * throws |PEG.Grammar.GrammarError|. The checks are run in sequence in order + * of their definition. */ - compileParser: function(grammar, startRule) { + _checks: [ + /* Checks that all referenced rules exist. */ + function(ast, startRule) { + for (var rule in ast) { + ast[rule].checkReferencedRulesExist(ast); + } + }, + + /* Checks that the start rule is defined. */ + function(ast, startRule) { + if (ast[startRule] === undefined) { + throw new PEG.Grammar.GrammarError( + "Missing \"" + startRule + "\" rule." + ); + } + }, + + /* Checks that no left recursion is present. */ + function(ast, startRule) { + for (var rule in ast) { + ast[rule].checkNoLeftRecursion(ast, []); + } + } + ], + + /* + * Generates a parser from a specified grammar AST and start rule. Throws + * |PEG.Grammar.GrammarError| if the AST contains a semantic error. Note that + * not all errors are detected during the generation and some may protrude to + * the generated parser and cause its malfunction. + */ + compileParser: function(ast, startRule) { /* * This ensures that the same grammar and start rule always generate exactly * the same parser. */ this._resetUniqueIdentifierCounters(); + for (var i = 0; i < this._checks.length; i++) { + this._checks[i](ast, startRule); + } + var parseFunctionDefinitions = []; - for (var rule in grammar) { - parseFunctionDefinitions.push(grammar[rule].compile()); + for (var rule in ast) { + parseFunctionDefinitions.push(ast[rule].compile()); } var source = this.formatCode(