ParseKit动作中的自定义对象

时间:2014-03-26 15:25:31

标签: objective-c parsekit pegkit

我对向ParseKit语法添加动作的能力非常感兴趣。关于这些行动中可用的内容的文件很少令人惊讶。说我有两个规则,如:

databaseName        = Word;
createTableStmt     ='CREATE' ('TEMP'| 'TEMPORARY')? 'TABLE' 'IF NOT EXISTS'? databaseName;

这显然不是一个完整的语法,而是作为一个例子。在解析时,我想“返回”具有某些属性的CreateTableStmt对象。如果我正确理解了该工具,我会在规则中添加一个动作,然后将其推送到组件上,该组件将随身携带它以供下一个处理或使用的规则。

所以例如它看起来像:

createTableStmt     ='CREATE' ('TEMP'| 'TEMPORARY')? 'TABLE' 'IF NOT EXISTS'? databaseName;
{
    AnotherObj* dbName = Pop(); //gives me the top most object
    CreateTableStmt* createTable = [[CreateTableStmt alloc] initWith:dbName];
    //set if it was temporary
    // set 'IF NOT EXISTS'
    PUSH(createTable);//push back on stack for next rule to use
}

然后当解析所有内容时,我可以从堆栈中获取该根对象,它是一个完全实例化的语法自定义表示。如果我没记错的话,有点像建立一个AST。然后,我可以比使用传入的字符串更容易地使用该表示。

我的问题是如何查看它是否与('TEMP' | 'TEMPORARY')匹配,以便我可以设置该值。这些令牌是否在堆栈中?有没有比回到'CREATE'更好的方法,看看我们是否通过了它。我是否应该在每场比赛中回弹到堆栈的底部?

另外,如果我的规则是

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

假设在匹配规则之前不会调用操作是否正确?因此,在这种情况下,当向堆栈调用操作时可能如下所示:

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)

这些正确的评估吗?有关于行动的其他文件吗?我知道它主要基于Antlr,但其微妙的差异可以让你遇到麻烦。

1 个答案:

答案 0 :(得分:4)

ParseKit的创作者。一些项目:

ParseKit弃用:

就在本周,我已将ParseKit分叉到一个名为PEGKit的更清洁/更小/更快的库中。 ParseKit应被视为已弃用,并且PEGKit应用于所有新开发。请转到PEGKit。

PEGKit几乎与ParseKit 的语法和代码生成功能相同,并且您的ParseKit语法可与PEGKit一起使用,只需进行一些小的更改。事实上,您的问题中的所有示例都可以使用而不会改变PEGKit。

See the Deprecation Notice in the ParseKit README

这是tutorial on PEGKit

语法中的语法错误:

我在上面的语法示例中发现了3个语法错误(这同样适用于ParseKit和PEGKit)。

  1. 这一行:

    createTableStmt     ='CREATE' ('TEMP'| 'TEMPORARY')? 'TABLE' 'IF NOT EXISTS'? databaseName;

    应该是:     

    createTableStmt     ='CREATE' ('TEMP'| 'TEMPORARY')? 'TABLE' ('IF' 'NOT' 'EXISTS')? databaseName;

    注意将无效'IF NOT EXISTS'构造分解为单个文字标记。这不仅是必要的,而且也是可取的,因此允许在单词之间使用可变的空格。

  2. POP()宏应全部大写

  3. 您的createTableStmt规则最后缺少分号(在操作结束后})。

  4. 在回答之前:

    确保您使用的是v0.3.1 PEGKit或更高版本(HEAD of master)。我找到了一个重要的错误,同时找到了你的问题的答案,我的解决方案需要这个解决方案。

    回答你的第一个问题:

      

    我的问题是如何才能看到它是否匹配('TEMP'|'TEMPORARY')所以我可以设置值?

    好问题!您在上面的进一步评论中基本上都有正确的想法。

    具体来说,我可能会将createTableStmt规则分解为4条规则:

    createTableStmt = 'CREATE'! tempOpt 'TABLE'! existsOpt databaseName ';'!;
    
    databaseName = QuotedString;
    
    tempOpt 
        = ('TEMP'! | 'TEMPORARY'!)
        | Empty
        ;
    
    existsOpt 
        = ('IF'! 'NOT'! 'EXISTS'!)
        | Empty
        ;
    
    • 请注意丢弃不需要的文字代币的所有重要!丢弃指令。

    • 另请注意,我已将最后两条规则更改为使用| Empty而非?。这样我就可以为空替代品添加动作(你会在一秒内看到)。

    然后,您可以将操作添加到语法中,或者如果您更喜欢使用纯代码,则可以使用ObjC 解析器委托回调

    如果您在语法中使用操作,则以下内容将起作用:

    createTableStmt = 'CREATE'! tempOpt 'TABLE'! existsOpt databaseName ';'!
    {
        NSString *dbName = POP();
        BOOL ifNotExists = POP_BOOL();
        BOOL isTemp = POP_BOOL();
        NSLog(@"create table: %@, %d, %d", dbName, ifNotExists, isTemp);
        // go to town
        // myCreateTable(dbName, ifNotExists, isTemp);
    };
    
    databaseName = QuotedString
    {
        // pop the string value of the `PKToken` on the top of the stack
        NSString *dbName = POP_STR();
        // trim quotes
        dbName = [dbName substringWithRange:NSMakeRange(1, [dbName length]-2)];
        // leave it on the stack for later
        PUSH(dbName);
    };
    
    tempOpt 
        = ('TEMP'! | 'TEMPORARY'!) { PUSH(@YES); }
        | Empty { PUSH(@NO); }
        ;
    
    existsOpt 
        = ('IF'! 'NOT'! 'EXISTS'!) { PUSH(@YES); }
        | Empty { PUSH(@NO); }
        ;
    

    added this Grammar and a test case to the PEGKit project

    至于你的第二个问题,请将其作为一个新的SO问题进行分解,并将其标记为ParseKitPEGKit,我会尽快找到它。