Distinguish between object($.book) and array ($.book[0]) in JavaCC jj file

48 Views Asked by At

I'm writing a jsonpath parser with JavaCC and need to distinguish between a simple object(such as $.book) and an array (such as $.book[0]), here is how the jjt file goes:

void Root() #void: {}
{
    <ROOT><SEPARATOR> Expression() (<EOL>)?  # starts with $.
}

void Expression() #void: {}
{
    Variable()      
    (
       <SEPARATOR>
       Variable()
    )*
}

void Variable() #void: {}   # here is where the error resides
{
    LOOKAHEAD(Object())     # a choice conflict between book and book[0]
    Object()
    |
     Array()
}

void Object():
{
    Token t;
}
{
     t =  <IDENTIFIER>      # such as $.book
    {
        jjtThis.setName(t.image);
    }
}

void Array():
{
    Token t;
}
{
    t =  <IDENTIFIER>                         # such as book[0]
    {
        jjtThis.setName(t.image);
    }
    <BRACKET_OPEN>t=<INTEGER_LITERAL><BRACKET_CLOSE>      # such as [0]
    {
        jjtThis.setIndex(java.lang.Integer.parseInt(t.image));
    }
}

With the generated JsonPathParser, it does not work as expected:

SimpleNode node = new JsonPathParser(new StringReader("$.book[0]")).parse();
SimpleNode child = simpleNode.jjtGetChild(0);
assertTrue(child instanceof ASTArray);  # assert failure, child is of type ASTObject

I understand that the root cause resides here: LOOKAHEAD(Object()) Object() | Array(), but I could not make it correct.

Tried to use t = <IDENTIFIER>(?!\[) in Object() method to make sure it's not followed by a [, but got a org.javacc.jjtree.ParseException: Encountered " "?" "? "" at line 96, column 27 error.

Any help is highly appreciated.

2

There are 2 best solutions below

0
aqingsao On

Finally work it out. In one word we have to use "semantic lookahead" to achieve this. A quick answer is shown as below, and for details please read this article: https://www.cs.purdue.edu/homes/hosking/javacc/doc/lookahead.html

void Variable() #void:
{
    Token t;
}
{
   ( LOOKAHEAD( Object(), { getToken(2).kind != BRACKET_OPEN }) 
        Object()
    |
     Array()
   )
}

0
Maurice Perry On

How about something like this:

void Root() #void: {}
{
    <ROOT> Selector() (<EOL>)?  # starts with $.
}

void Selector() #void: {}
{
    Field()
    (
       Field() | Index()
    )*
}

void Field():
{
    Token t;
}
{
    <SEPARATOR> t =  <IDENTIFIER>      # such as $.book
    {
        jjtThis.setName(t.image);
    }
}

void Index():
{
    Token t;
}
{
    <BRACKET_OPEN>t=<INTEGER_LITERAL><BRACKET_CLOSE>      # such as [0]
    {
        jjtThis.setIndex(java.lang.Integer.parseInt(t.image));
    }
}