Search code examples
parsingparsekitpegkit

Parser Stack management during grammar with actions


This is the second question related to Custom objects in ParseKit Actions

If I had a grammar rule such as:

qualifiedTableName  = (databaseName '.')? tableName (('INDEXED' 'BY' indexName) | ('NOT' 'INDEXED'))?;

Is it correct to assume that the action would not be called until the rule had been matched? So in this case when the action is called to the stack could look like:

possibly:
|'INDEXED'
|'NOT'
or:
|indexName (A custom object possibly)
|'BY'
|'INDEXED

|tableName (for sure will be here)

and possibly these
|'.'            (if this is here I know the database name must be here) if not push last one on?
|databaseName
--------------(perhaps more things from other rules)

Are these correct assessments? Is there any other documentation on actions? I know it is heavily based on Antlr but its the subtle differences that can really get you in trouble.


Solution

  • Creator of PEGKit here.

    Actions are executed immediately after the preceeding token has been matched.

    Assume this input:

    mydb.mytable INDEXED BY 'foo'
    

    Your example rule didn't contain any Actions, so I'll add some. It's really much easier to add actions if you break down your rule into smaller subrules:

    qualifiedTableName = name indexOpt
    {
        // now stack contains 3 `NSString`s. 
        // ["mydb", "mytable", "foo"]
        NSString *indexName = POP();
        NSString *tableName = POP();
        NSString *dbName = POP();
        // do stuff here
    };
    
    databaseName = Word;
    tableName = Word;
    indexName = QuotedString;
    
    name = (databaseName '.'!)? tableName 
    {
        // now stack contains 2 `PKToken`s of type Word
        // [<Word «mydb»>, <Word «mytable»>]
        // pop their string values
        NSString *tableName = POP_STR();
        NSString *dbName = POP_STR();
        PUSH(dbName);
        PUSH(tableName);
    };
    
    indexOpt
        = index
        | Empty { PUSH(@""); }
        ;
    
    index
        = ('INDEXED'! 'BY'! indexName)
        { 
            // now top of stack will be a Quoted String `PKToken`
            // […, <Quoted String «"foo"»>]
            // pop its string value
            NSString *indexName = POP_STR();
            // trim quotes
            indexName = [indexName substringWithRange:NSMakeRange(1, [indexName length]-2)];
            // leave it on the stack for later
            PUSH(indexName);
        }
        | ('NOT'! 'INDEXED'!) { PUSH(@""); }
        ;
    

    Note that I am discarding all of the literal tokens using the ! discard directive. These literals are pure syntax, and are not needed in your Actions to do further processing.

    Also note that in the case of either a non-existent INDEXED BY expression or a NOT INDEXED expression, I'm pushing an empty string onto the stack. This is so that you can uniformly handle the specified index in the qualifiedTableName Action. In that Action, you will always have a string on the top of the stack which specifies the index. If it is an empty string, then there is no index.