Пользовательские объекты в действиях 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).
Эта строка:
createTableStmt = 'CREATE' ('TEMP'| 'TEMPORARY')? "ТАБЛИЦА" "ЕСЛИ НЕ СУЩЕСТВУЕТ"? Databasename;
Должно быть:
createTableStmt = 'CREATE' ('TEMP'| 'TEMPORARY')? 'TABLE' ('ЕСЛИ' НЕ '' СУЩЕСТВУЕТ ')? Databasename;
Обратите внимание на распад инвалида
'IF NOT EXISTS'
построить в отдельных буквенных токенов. Это не только необходимо, но и желательно, чтобы допускался переменный пробел между словами.POP()
макрос должен быть в верхнем регистре.Ваш
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
и я доберусь до этого как можно скорее.