grappa icon indicating copy to clipboard operation
grappa copied to clipboard

firstOf() ignores overridden fromStringLiteral(String)

Open akirschbaum opened this issue 10 years ago • 1 comments

The following grammar contains three rules. Each of these rules should match inputs "a" or "b", optionally followed by spaces.

rule1 explicitly matches the trailing whitespace with zeroOrMore(sp()). This rule matches the input "a".

rule2 implicitly matches the trailing whitespace by overriding fromStringLiteral(String) and adding zeroOrMore(sp()). That is: "x" matches this string; "x " matches sequence("x", zeroOrMore(sp())). This rule does NOT match the input "a". This is incorrect.

rule3 is the same as rule2 but also matches cr(). This slight change causes the rule to match the input "a".

Note: it seems that the actual change in rule3 is not important: If at least one non-String argument is present, the rule does match the input "a".

The issue seems to be that if all arguments of firstOf() are string literals, the overloaded fromStringLiteral(String) is ignored.

$ gradle
:compileJava
:processResources UP-TO-DATE
:classes
:run
rule1: true
rule2: false
rule3: true

BUILD SUCCESSFUL

Total time: 5.954 secs
$ cat build.gradle
defaultTasks 'run'

apply plugin: 'application'
apply plugin: 'java'

sourceCompatibility = 1.8
mainClassName = 'app.Main'

dependencies {
    compile 'com.github.fge:grappa:2.0.0'
}

repositories {
    mavenCentral()
}
$ cat src/main/java/app/Main.java
package app;

import com.github.fge.grappa.Grappa;
import com.github.fge.grappa.buffers.CharSequenceInputBuffer;
import com.github.fge.grappa.parsers.BaseParser;
import com.github.fge.grappa.rules.Rule;
import com.github.fge.grappa.run.ListeningParseRunner;

public class Main {

    public static void main(final String[] args) {
        final Parser1 parser = Grappa.createParser(Parser1.class);
        final CharSequenceInputBuffer buffer = new CharSequenceInputBuffer("a");
        System.out.println("rule1: "+new ListeningParseRunner<>(parser.rule1()).run(buffer).isSuccess());
        System.out.println("rule2: "+new ListeningParseRunner<>(parser.rule2()).run(buffer).isSuccess());
        System.out.println("rule3: "+new ListeningParseRunner<>(parser.rule3()).run(buffer).isSuccess());
    }

    public static class Parser1 extends BaseParser<Object> {

        public Rule rule1() {
            return firstOf(sequence(fromStringLiteral("a"), zeroOrMore(sp())), sequence(fromStringLiteral("b"), zeroOrMore(sp())));
        }

        public Rule rule2() {
            return firstOf("a ", "b ");
        }

        public Rule rule3() {
            return firstOf("a ", "b ", cr());
        }

        @Override
        protected Rule fromStringLiteral(final String string) {
            return string.endsWith(" ") ? sequence(super.fromStringLiteral(string.substring(0, string.length()-1)), zeroOrMore(sp())) : super.fromStringLiteral(string);
        }

    }

}

akirschbaum avatar May 30 '15 17:05 akirschbaum

A better example for rule3 would be: firstOf("a ", "b ", NOTHING).

akirschbaum avatar May 30 '15 18:05 akirschbaum