Не имея полной спецификации синтаксиса, который вы пытаетесь проанализировать, трудно дать точный ответ.Далее я предполагаю, что это единственные два места, где наличие (или отсутствие) пробела между двумя токенами влияет на синтаксический анализ.
Различие между f(...)
и f (...)
происходит неожиданноколичество языков.Одна из распространенных стратегий состоит в том, чтобы лексер распознавал идентификатор, за которым сразу следует открывающая скобка, как токен «FUNCTION_CALL».
Вы найдете это, например, в большинстве awk
реализаций;в awk неоднозначность между вызовом функции и конкатенацией разрешается путем требования, чтобы открытая скобка в вызове функции следовала сразу за идентификатором.Аналогично, директива определения макроса препроцессора C различает #define foo(A) A
(определение макроса с аргументами) и #define foo (A)
(обычный макрос, расширение которого начинается с токена (
.
Если выДелая это с (f) lex, вы можете использовать оператор конечного контекста /
:
[[:alpha:]_][[:alnum:]_]*/'(' { yylval = strdup(yytext); return FUNC_CALL; }
[[:alpha:]_][[:alnum:]_]* { yylval = strdup(yytext); return IDENT; }
Грамматика теперь довольно проста:
call: FUNC_CALL '(' expression_list ')' /* foo(1, 2) */
| IDENT expression_list /* foo (1, 2) */
| IDENT /* foo * 3 */
ЭтоРазличие не будет полезным во всех синтаксических контекстах, поэтому часто бывает полезно добавить нетерминал, который будет соответствовать любой форме идентификатора:
name: IDENT | FUNC_CALL
Но вам нужно быть осторожным с этим нетерминаломВ частности, использование его как части грамматики выражения может привести к конфликтам синтаксического анализатора. Но в других контекстах это будет хорошо:
func_defn: "def" name '(' parameters ')' block "end"
(я знаю, что это не точный синтаксис дляОпределения функций Ruby. Это только для иллюстративных целей.)
Еще одна проблема вызывает еще одну двусмысленность, в которой оказывается, что унарные операторы +
и -
должны bВ определенных обстоятельствах он рассматривается как часть целочисленного литерала.Поведение синтаксического анализатора Ruby предполагает, что лексер объединяет символ знака с сразу следующим числом в случае, когда он может быть первым аргументом функции.(То есть в контексте <identifier><whitespace><sign><digits>
, где <identifier>
не является уже объявленной локальной переменной.)
Этот вид контекстного правила, безусловно, может быть добавлен в лексический сканер с использованием условий запуска, хотя это больше, чемнемного некрасивоНе полностью реализованная реализация, основанная на предыдущем:
%x SIGNED_NUMBERS
%%
[[:alpha:]_][[:alnum:]_]*/'(' { yylval.id = strdup(yytext);
return FUNC_CALL; }
[[:alpha:]_][[:alnum:]_]*/[[:blank:]] { yylval.id = strdup(yytext);
if (!is_local(yylval.id))
BEGIN(SIGNED_NUMBERS);
return IDENT; }
[[:alpha:]_][[:alnum:]_]*/ { yylval.id = strdup(yytext);
return IDENT; }
<SIGNED_NUMBERS>[[:blank:]]+ ;
/* Numeric patterns, one version for each context */
<SIGNED_NUMBERS>[+-]?[[:digit:]]+ { yylval.integer = strtol(yytext, NULL, 0);
BEGIN(INITIAL);
return INTEGER; }
[[:digit:]]+ { yylval.integer = strtol(yytext, NULL, 0);
return INTEGER; }
/* ... */
/* If the next character is not a digit or a sign, rescan in INITIAL state */
<SIGNED_NUMBERS>.|\n { yyless(0); BEGIN(INITIAL); }
Другим возможным решением для лексера будет различать знаковые символы, которые следуют за пробелом и непосредственно следуют за цифрой, а затем позволяютпарсер пытается выяснить, должен ли знак сочетаться со следующим числом.Однако это все равно будет зависеть от способности различать локальные переменные и другие идентификаторы, что по-прежнему потребует лексической обратной связи через таблицу символов.
Стоит отметить, что конечным результатом всех этих усложнений является языкчья семантика не очень очевидна в некоторых угловых случаях.Тот факт, что f+3
и f +3
дают разные результаты, может легко привести к незначительным ошибкам, которые могут быть очень трудно обнаружить.Во многих проектах, использующих языки с такими неоднозначностями, руководство по стилю проекта будет запрещать юридические конструкции с неясной семантикой.Возможно, вы захотите принять это во внимание при разработке вашего языка, если вы этого еще не сделали.