1

我对向 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,但它的细微差别确实会给你带来麻烦。

4

1 回答 1

4

ParseKit的创建者在这里。几个项目:

ParseKit 弃用:

就在本周,我将 ParseKit 分叉到了一个更干净/更小/更快的库,称为PEGKit。ParseKit 应被视为已弃用,PEGKit 应用于所有新开发。请移至 PEGKit。

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

请参阅 ParseKit README 中的弃用通知

还有这个关于 PEGKit 的教程

语法中的语法错误:

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

  1. 这一行:

    createTableStmt ='CREATE'('TEMP'|'TEMPORARY')?'表''如果不存在'?数据库名称;

    应该:

    createTableStmt ='CREATE'('TEMP'|'TEMPORARY')?“表”(“如果”“不”“存在”)?数据库名称;

    请注意将无效'IF NOT EXISTS'构造分解为单独的文字标记。这不仅是必要的,而且是可取的,以便允许单词之间的可变空格。

  2. POP()应该全部大写

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

回答前:

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

回答你的第一个问题:

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

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

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

createTableStmt = 'CREATE'! tempOpt 'TABLE'! existsOpt databaseName ';'!;

databaseName = QuotedString;

tempOpt 
    = ('TEMP'! | 'TEMPORARY'!)
    | Empty
    ;

existsOpt 
    = ('IF'! 'NOT'! 'EXISTS'!)
    | Empty
    ;
  • 请注意所有用于丢弃不需要的文字标记的重要!丢弃指令。

  • 另请注意,我已将最后两条规则更改为使用| Empty而不是?. 这样我就可以将 Actions 添加到 Empty 替代品中(稍后您会看到)。

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

如果您在语法中使用Actions,则如下所示的内容将起作用:

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); }
    ;

我已将此语法和一个测试用例添加到 PEGKit 项目中。

至于您的第二个问题,请将其分解为一个新的 SO 问题,并标记它ParseKitPEGKit我会尽快解决。

于 2014-03-27T16:35:07.857 回答