Как правильно сканировать идентификаторы с помощью Ragel
Я пытаюсь написать сканер для своего C/C++/C#/Java/D-подобного языка программирования, который я проектирую по личным причинам. Для этой задачи я использую Ragel для генерации моего сканера. У меня возникают проблемы с пониманием, когда именно многие операторы инициируют действия, возможно, потому, что мои ученые были сосредоточены на практических знаниях, а не на теории, и большая часть этого недетерминированного / детерминированного бизнеса с конечными автоматами идет мне на ум. Я считаю, что документация либо отсутствует, либо мое понимание таковым является. Я предполагаю последнее.
В любом случае, я работаю над основами. Я определил несколько ключевых слов и специальных символов в моей первой итерации. Теперь я столкнулся с проблемой, когда все ключевые слова сканируются как идентификаторы. Я использую оператор сканера для всех моих ключевых слов, так как это решило мою проблему со строкой returns
сканируется как оба return
а также returns
ключевое слово.
Как правильно сканировать идентификаторы? Я понимаю, что для того, чтобы сделать это детерминированным, мне нужно эффективно указать, что лексема может быть только identifier
если он не соответствует ни одному другому шаблону токена. Прости мое отсутствие знаний.
Скрипт Ragel:
%%{
Identifier = (alpha | '_') . (alnum | '_')*;
action IdentifierAction
{
std::cout << "identifier(\"";
std::cout.write(ts, te - ts);
std::cout << "\")";
}
}%%
%%{
main :=
|*
Interface => InterfaceAction;
Class => ClassAction;
Property => PropertyAction;
Function => FunctionAction;
TypeQualifier => TypeQualifierAction;
OpenParenthesis => OpenParenthesisAction;
CloseParenthesis => CloseParenthesisAction;
OpenBracket => OpenBracketAction;
CloseBracket => CloseBracketAction;
OpenBrace => OpenBraceAction;
CloseBrace => CloseBraceAction;
Semicolon => SemicolonAction;
Returns => ReturnsAction;
Return => ReturnAction;
Identifier => IdentifierAction;
space+;
*|;
}%%
1 ответ
Не знаком с Ragel, но сделал несколько пользовательских анализаторов и сканеров.
Похоже, ваш вопрос больше относится к обнаружению ключевых слов, чем к определению общих идентификаторов.
У вас есть правила, предписывающие Ragel определять, когда в разделе есть код, число, ключевое слово "return", точка с запятой, ключевое слово "return", идентификатор и так далее. Хотя для каждого ключевого слова можно создать правило, я не рекомендую.
Что я узнал из опыта, так это то, что лучше читать все ключевые слова как идентификаторы (назначить общий токен "идентификатор"), а в некоторой части кода C/C++ определять, какие идентификаторы являются "ключевыми словами".
Другими словами. Ragel будет обнаруживать только идентификаторы. "myvar", "return" и "return" будут помечены как "идентификаторы". Позже, в коде вашего семантического действия (C/C++, а не Ragel), вы проверите каждый идентификатор и определите, является ли ключевое слово в C/C++. Обычно это делается с помощью списка ключевых слов.
Я думаю, что это будет что-то вроде этого:
%%{
Identifier = (alpha | '_') . (alnum | '_')*;
action IdentifierAction
{
String Keywords[] =
(
"return",
"if",
"else"
);
String MyIdentifier = te - ts;
if (SearchKeywordCode(Keywords, MyIdentifier)) {
std::cout << "keyword(\"";
std::cout.write(ts, te - ts);
std::cout << "\")";
}
else {
std::cout << "identifier(\"";
std::cout.write(ts, te - ts);
std::cout << "\")";
}
}
}%%
Таким образом, не существует правила "Возврат" или "Возврат", а только "Идентификатор".