nearley icon indicating copy to clipboard operation
nearley copied to clipboard

Match non-terminal other than the default (first)

Open averynortonsmith opened this issue 4 years ago • 2 comments

From the docs:

By default, nearley attempts to parse the first nonterminal defined in the grammar.

Is there a way to parse starting at a non-terminal other than the default? The phrasing suggests that there is but I don't see an explanation on how to accomplish this in the docs or FAQ.

node 12.18.2 npx 6.14.5 nearley 2.19.7

averynortonsmith avatar Nov 22 '20 03:11 averynortonsmith

I think I also ran into the same issue. Depending on a certain condition I wanted to use a different entry point and return a result object of a different type.

Grammar constructor seems to accept a start argument to provide a rule name, but it didn't work for me. And it is also doesn't seem to be documented in the types package.

I end up with a workaround like this:

import { Parser, Grammar } from 'nearley';
import compiledRules from './grammar';

const compiledRulesAlt = { ...compiledRules, ParserStart: 'altMain' };

const compiledRules1 = (condition) ? compiledRules : compiledRulesAlt;
const parser = new Parser(Grammar.fromCompiled(compiledRules1));

// Expected following to work but it didn't:
// const parserStart = (condition) ? 'main' : 'altMain';
// const parser = new Parser(Grammar.fromCompiled(compiledRules, parserStart));

KillyMXI avatar Jun 28 '21 19:06 KillyMXI

Both Parser and Grammar.fromCompiled allow you to specify an optional start element.

Specifying a start element with Parser works only if you let Parser create the Grammar object, i.e., you can do this:

	    const parser = new nearley.Parser(Lodestone,'rhs');

but not this:

	    const parser = new nearley.Parser(nearley.Grammar.fromCompiled(Lodestone),'rhs');

Looking at the top of Parser, the code ignores start if the first input is a Grammar:

    function Parser(rules, start, options) {
        if (rules instanceof Grammar) {
            var grammar = rules;
            var options = start;
        } else {

This seems to be intentional, but I fixed it to respect start this way:

    function Parser(rules, start, options) {
        if (rules instanceof Grammar) {
            var grammar = rules;
            // var options = start;
	    rules.start = start || rules.start || rules.ParserStart;
        } else {

But this fix is not backwards-compatible to anyone who was calling Parser(rules, options) and relying on the old behavior.

Specifying start with Grammar.fromCompiled doesn't work because the compiled grammar (i.e., the .js file created from a .ne) always sets ParserStart to the first symbol in the grammar. So the test in this code:

    Grammar.fromCompiled = function(rules, start) {
        var lexer = rules.Lexer;
        if (rules.ParserStart) {
            start = rules.ParserStart;
            rules = rules.ParserRules;
        }

is always true, and start always gets reset to rules.ParserStart.

My fix:

    Grammar.fromCompiled = function(rules, start) {
        var lexer = rules.Lexer;
	start = start || rules.ParserStart;
	rules = rules.ParserRules || rules;

which essentially says that start overrules rules.ParserStart. That seems reasonable to me.

srt19170 avatar Nov 11 '21 21:11 srt19170