Пользовательские объекты в действиях ParseKit

Я очень заинтригован способностью добавлять действия в грамматику 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') так что я могу установить значение. Эти токены в стеке? Есть ли лучший способ, чем вернуться к "СОЗДАТЬ" и посмотреть, прошли ли мы его. Должен ли я в любом случае возвращаться к нижней части стека в каждом матче?

Кроме того, если бы мое правило было вместо

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 ответ

Решение

Создатель ParseKit здесь. Несколько предметов:

ParseKit не рекомендуется

Только на этой неделе я разложил ParseKit в более чистую / меньшую / более быструю библиотеку под названием PEGKit. ParseKit следует считать устаревшим, а PEGKit следует использовать для всех новых разработок. Пожалуйста, перейдите к PEGKit.

PEGKit практически идентичен грамматике и функциям генерации кода в ParseKit, и ваши грамматики ParseKit можно использовать с PEGKit с небольшими изменениями. На самом деле, все приведенные здесь примеры пригодны для использования без изменений в PEGKit.

См. Уведомление об устаревании в README ParseKit.

И этот учебник по PEGKit.

Синтаксические ошибки в вашей грамматике:

В приведенных выше примерах грамматики я обнаружил 3 синтаксические ошибки (это в равной степени относится и к ParseKit, и к PEGKit).

  1. Эта строка:

     createTableStmt = 'CREATE' ('TEMP'| 'TEMPORARY')? "ТАБЛИЦА" "ЕСЛИ НЕ СУЩЕСТВУЕТ"? Databasename; 

    Должно быть:

     createTableStmt = 'CREATE' ('TEMP'| 'TEMPORARY')? 'TABLE' ('ЕСЛИ' НЕ '' СУЩЕСТВУЕТ ')? Databasename; 

    Обратите внимание на распад инвалида 'IF NOT EXISTS' построить в отдельных буквенных токенов. Это не только необходимо, но и желательно, чтобы допускался переменный пробел между словами.

  2. POP() макрос должен быть в верхнем регистре.

  3. Ваш createTableStmt Правило пропускает точку с запятой в самом конце (после закрытия действия }).

Прежде чем ответить:

Убедитесь, что вы используете PEGKit v0.3.1 или новее (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, если вы предпочитаете работать в чистом коде.

Если вы используете 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 и отметьте его ParseKit а также PEGKit и я доберусь до этого как можно скорее.

Другие вопросы по тегам