Я работаю над проектом по преобразованию сценариев, написанных на языке LSL Second Life, в Lua. Я изучаю flex и btyacc для этого проекта. На самом деле, это гораздо больший проект, это только часть его.
http://www.sqlite.org/cgi/src
В качестве первого шага я хочу убедиться, что производимый мной AST является точным отражением входных данных. Поэтому моя идея состоит в том, чтобы создать новый файл из этого AST, а затем сравнить их. Это означает, что мне нужно включить пробел и комментарии в AST, чтобы при использовании его для гееренирования файла результатов он содержал точно такой же пробел и комментарии.
У меня были проблемы с пробелами. Искать и экспериментировать в течение нескольких дней и никуда не деться Каждый пример, который я вижу, просто игнорирует пустое пространство, а не сохраняет его для дальнейшего использования. Полагаю, у меня возникнет точно такая же проблема с комментариями, так как они в основном просто еще одна форма пространства, которую следует игнорировать.
Я бы подумал, что это стандартная вещь, но я просто не могу найти никаких примеров. Кто-нибудь знает примеры подобных вещей?
Исходный код находится на github, если кто-то хочет просмотреть его и предложить подход.
https://github.com/onefang/SledjHamr/blob/experimental/LuaSL/src LuaSL_yaccer.l
, LuaSL_yaccer.y
, LuaSL_LSL_tree.h
и LuaSL_LSL_tree.c
У гибкой линии, которая распознает пробелы, действие закомментировано. Если я откомментирую, я получаю ошибку разбора.
РЕШЕНИЕ
Я использовал решение bdonlan, но я перевел проект на использование лимона вместо btyacc в середине его реализации. Это то, что я сделал. Исходный код ниже упрощен.
С помощью lemon вы создаете цикл, который вызывает лексер, затем берете результат вызова лексера и передаете его парсеру. Мой цикл каждый раз выделяет новую структуру yylval, эта структура содержит символьный указатель для пробела или комментария. Я также использую этот yylval в качестве структуры моего узла AST, так как он уже содержит большую часть того, что мне нужно, и избавляет от необходимости тратить время на перераспределение памяти или копирование вещей вокруг.
struct AST_node
{
struct AST_node *left;
struct AST_node *right;
char *white_space;
struct AST_token *token; /* common() stashes a pointer to a token structure here. */
int line, column;
union
{
/* The various types of symbols are stored here as per usual, including this one - */
int operationValue;
} value;
}
struct AST_node *lval = calloc(1, sizeof(struct AST_node);
while((yv = yylex(lval)) != 0)
{
Parse(pParser, yv, lval);
lval = calloc(1, sizeof(struct AST_node));
}
В лексере у меня есть функция common (), которая вызывается из каждого раздела действий с регулярными выражениями. Помимо прочего, если это комментарий или пробел, я сохраняю текст в статическом аккумуляторе. Если это не комментарий или пробел, я сохраняю аккумулятор (если он существует) в структуре yylval и очищаю аккумулятор. Этот аккумулятор собирает пробелы и комментарии, поэтому любой из них в yylval может содержать оба.
Лексер не возвращает символ, если это просто пробел / комментарии, поэтому он накапливает их до тех пор, пока не дойдет до испускания действительного символа.
В лимоне все терминалы и нетерминалы являются типом, используемым для yylval, поэтому я могу передать это коду C в разделах действия. Например -
expr(A) ::= expr(B) LSL_ADD(D) expr(C). { A = addOperation(D, LSL_ADD, B, C); }
LSL_ADD - это символ, излучаемый лексером, а D - это его значение, то есть yylval, который я создал в главном цикле для передачи лексеру. В этом случае addOperation () добавляет указатели на левый и правый узлы AST (B и C) в структуру AST (D) и помещает туда символ (так что я позже узнаю, что эта конкретная операция является дополнением) .
struct AST_node *addOperation(struct AST_node *lval, int type, struct AST_node *left, struct AST_node *right)
{
lval->left = left;
lval->right = right;
lval->value.operationValue = type;
return lval;
}
Позже, когда я восстанавливаю исходный код из AST, я просто выводю пробел / комментарий перед выводом символа в том же узле AST.
Да, я знаю, что в приведенном выше коде есть некоторое дублирование, нет необходимости хранить тип как в члене токена, так и в объединении. Я уберу это из своего кода позже. На данный момент это серверы, чтобы проиллюстрировать, что происходит.