Flex and Bison - грамматика, которая иногда заботится о пробелах - PullRequest
0 голосов
/ 08 февраля 2019

В настоящее время я пытаюсь реализовать грамматику, которая очень похожа на ruby ​​.Для простоты лексер в настоящее время игнорирует пробелы.

Однако в некоторых случаях буква пробела имеет большое значение:

def some_callback(arg=0)
    arg * 100
end

some_callback (1 + 1) + 1  # 300
some_callback(1 + 1) + 1   # 201
some_callback +1           # 100
some_callback+1            # 1
some_callback + 1          # 1

Поэтому в настоящее время лексер игнорирует все пробелы:

{WHITESPACE} { ; }

А язык говорит, например, что-то вроде:

UnaryExpression:
    PostfixExpression
  | T_PLUS UnaryExpression
  | T_MINUS UnaryExpression
  ;

Один из способов решения этой проблемы, который я могу придумать, заключается в явном добавлении пробелов во всю грамматику, но при этомпоэтому вся грамматика значительно усложнилась бы:

// OLD:
AdditiveExpression:
    MultiplicativeExpression
  | AdditiveExpression T_ADD MultiplicativeExpression
  | AdditiveExpression T_SUB MultiplicativeExpression
  ;

// NEW:
_:
    /* empty */
  | WHITESPACE _;

AdditiveExpression:
    MultiplicativeExpression
  | AdditiveExpression _ T_ADD _ MultiplicativeExpression
  | AdditiveExpression _ T_SUB _ MultiplicativeExpression
  ;

//...

UnaryExpression:
    PostfixExpression
  | T_PLUS UnaryExpression
  | T_MINUS UnaryExpression
  ;

Поэтому мне хотелось бы спросить, есть ли лучший метод решения этой грамматики.

Заранее спасибо!

1 Ответ

0 голосов
/ 08 февраля 2019

Не имея полной спецификации синтаксиса, который вы пытаетесь проанализировать, трудно дать точный ответ.Далее я предполагаю, что это единственные два места, где наличие (или отсутствие) пробела между двумя токенами влияет на синтаксический анализ.

Различие между 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 дают разные результаты, может легко привести к незначительным ошибкам, которые могут быть очень трудно обнаружить.Во многих проектах, использующих языки с такими неоднозначностями, руководство по стилю проекта будет запрещать юридические конструкции с неясной семантикой.Возможно, вы захотите принять это во внимание при разработке вашего языка, если вы этого еще не сделали.

...