Зубр сдвиг / уменьшить и уменьшить / уменьшить конфликт - PullRequest
0 голосов
/ 20 сентября 2018

Хорошо, я уже три раза пытался переписать эту грамматику бизонов и продолжаю сталкиваться с конфликтами сдвиг / уменьшение и уменьшение / уменьшение.Синтаксис, который я пытаюсь разобрать, выглядит следующим образом.Элементы в {...} предназначены для одного или другого.Элементы в [...] являются необязательными.

CONSTANT constant-name constant-class 
    constant-class = { EQUALS expression numeric-options } 
                     { EQUALS STRING string string-options } 
    numeric-options = [ PREFIX prefix-string ] 
                      [ TAG tag-string ]  
                      [ COUNTER #local-name ]
                      [ TYPENAME type-name ] ; 
    string-options = [ PREFIX prefix-string ] 
                     [ TAG tag-string ] ; 

CONSTANT (constant-name,...) EQUALS expression 
                 [ INCREMENT expression ] 
                 [ PREFIX prefix-string ] 
                 [ TAG tag-string ] 
                 [ COUNTER #local-name ] 
                 [ TYPENAME type-name ]; 

CONSTANT  constant-name EQUALS expression, 
                . 
                . 
                . 
 ;

CONSTANT  (constant-name,...) EQUALS expression, 
                . 
                . 
                . 
 ; 

У меня проблемы с получением последних трех для всей работы.Я могу заставить любого из них работать, но не всех 4 из них.У меня сейчас есть один сдвиг / уменьшить и один уменьшить / уменьшить конфликт.У меня есть следующая грамматика:

constant
    : SDL_K_CONSTANT constant_style ';'
    ;

constant_style
    : constant_name constant_class
    | constant_list
    | constant_set
    ;

constant_name
    : sdl_name
    ;

constant_class
    : SDL_K_EQUALS sdl_decimal
    | SDL_K_EQUALS SDL_K_STRING sdl_string
    ;

constant_names
    : constant_name
    | constant_names ',' constant_name
    | '(' constant_names ')'
    ;

names_equal
    : constant_names SDL_K_EQUALS sdl_decimal
    ;

constant_list
    : names_equal
    ;

constant_set
    : names_equal
    | constant_set ',' names_equal
    ;

Я думаю, что имена самодокументированы (по крайней мере, вы должны понимать типы).Любая помощь будет принята с благодарностью.У меня такое ощущение, что я либо слишком сильно упростилась, либо недостаточно.

ПРИМЕЧАНИЕ. Я выяснил, как редактировать свой пост, удалил параметры и изменил SDL_K_COMMA и SDL_K_SEMI на ',' и ';' соответственно.

Спасибо.

Вот несколько примеров, которые следует проанализировать:

CONSTANT block_node_size EQUALS 24;
CONSTANT Strcon EQUALS STRING "This is a string constant" PREFIX Jg$ 
#block_size = 24;
CONSTANT block_node_size EQUALS #block_size;
CONSTANT 
    xyz EQUALS 10, 
    alpha EQUALS 0, 
    noname EQUALS 63;
CONSTANT
    (zyx, nameless) EQUALS 10,
    (beta) EQUALS 1,
    gamma EQUALS 42;
CONSTANT ( 
    bits, 
    bytes, 
    words, 
    longs, 
    quads, 
    octas 
    ) EQUALS 0 INCREMENT 1 PREFIX ctx$;
CONSTANT 
    (bad_block,bad_data,,,, 
    overlay,rewrite) EQUALS 0 INCREMENT 4;
CONSTANT (pli,c,bliss,macro) 
    EQUALS 4 INCREMENT 4 PREFIX lang$ 
    COUNTER #lang;
CONSTANT (basic,pascal,fortran) 
    EQUALS #lang + 4 INCREMENT 4 PREFIX lang$;

Надеюсь, это поможет.

Кстати: вот EBNF (sortиз) для этого:

/*
 * Define the CONSTANT construct (Left out Expression).
 */
Str                 ::= "\"" Printable* "\""
Name                ::= "$" | "_" | [A-Za-z] ("$" | "_" | [A-Z0-9a-z])*
Names               ::= Name | ("(" Name ("," Name )* ")")
Constant_class      ::= "EQUALS" (Expression Numeric_options | "STRING" Str 
                        String_options)
String_options      ::= Prefix? Tag?
Numeric_options     ::= String_options Counter? Typename? Radix?
Increment_options   ::= Increment? Numeric_options
Constant_list       ::= Names "EQUALS" Expression Increment_options
Constant_set        ::= Names Equals Expression
                        ("," Names "EQUALS" Expression)*
Constant            ::= "CONSTANT" (Name Constant_class | Constant_list | 
                        Constant_set) ";"?
Prefix              ::= "PREFIX" Str
Tag                 ::= "TAG" Str
Radix               ::= "RADIX" ("DEC" | "OCT" | "HEX")
Counter             ::= "COUNTER" Variable
Increment           ::= "INCREMENT" Expression
Typename            ::= "TYPENAME" Name

Я думаю, что это об этом.

1 Ответ

0 голосов
/ 21 сентября 2018

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

Основная проблема - двусмысленность в ваших грамматических спецификациях.Одна из них может быть просто ошибкой: в соответствии с вашими шаблонами левая часть EQUAL выглядит либо как отдельный name, либо как разделенный запятыми список name, заключенный в скобки.Однако ваша грамматика допускает разделенный запятыми список из name s, с возможностью того, что первый (или единственный) элемент в списке может быть списком имен в скобках:

constant_names
    : constant_name
    | constant_names ',' constant_name
    | '(' constant_names ')

Это будет соответствоватьa, a, b, (a, b), (a, b), c и (a, b), c, d.Но я думаю, что только первый и третий из них на самом деле предназначены.

В любом случае у вас есть два производства:

constant_style
    : constant_name constant_class
    | constant_list

Для первого у нас есть:

constant_class
    : SDL_K_EQUALS sdl_decimal

в то время как для второго у нас есть:

constant_list: names_equal
names_equal
    : constant_names SDL_K_EQUALS sdl_decimal

Поскольку constant_name может соответствовать одному name, есть два различных способа сопоставления answer = 42, которые неизбежно приведут кконфликт синтаксического анализа.

Но есть еще один возможный синтаксический анализ для answer = 42:

constant_set
    : names_equal

Итак, давайте начнем с упрощения всего этого, а затем мы можем вернуться к чему (возможно,быть) ваша первоначальная цель.Идея состоит в том, чтобы учесть все, что синтаксически похоже:

constant-stmt  : "CONSTANT" clause-list ';'
clause-list    : clause | clause-list ',' clause
clause         : left-hand-side "EQUALS" right-hand-side
left-hand-side : name | '(' name-list ')'
name-list      : name | name-list ',' name
right-hand-side: expression   /* See below */

Я надеюсь, что все достаточно просто для понимания.

Но мы можем видеть из оригинала, что (по крайней мере, при определенных обстоятельствах)), для right-hand-side есть гораздо больше опций, чем показано в приведенном выше фрагменте.Было бы тривиально просто добавить другие синтаксисы к right-hand-side.Тем не менее, похоже, что предполагается, что эти дополнительные параметры доступны только в том случае, если существует одно предложение.В этом случае мы могли бы сделать что-то вроде этого:

constant-stmt  : "CONSTANT" constant-body ';'
constant-body  : complex-clause | clause-list
clause-list    : clause | clause-list ',' clause
clause         : left-hand-side "EQUALS" right-hand-side
right-hand-side: expression
complex-clause : left-hand-side "EQUALS" complex-rhs
complex-rhs    : expression numeric-options
               | "STRING" string-literal string-options

Но, к сожалению, это снова двусмысленно, потому что numeric-options может быть пустым, поэтому expression будет соответствовать right-hand-side и complex-right-hand-side.

На практике эта двусмысленность не имеет значения.Нет семантической разницы между объявлением name EQUALS expression как единственным объявлением в объявлении CONSTANT или одним из списка таких объявлений.Таким образом, одна возможность - просто игнорировать возникающий конфликт уменьшения / уменьшения, возможно, поместив %expect 1 в файл.Но это не очень приятно на самом деле.Поэтому мы попытаемся устранить эту проблему.

Один из способов - настаивать на том, чтобы у первых complex-rhs был хотя бы один numeric-option.Но это будет действительно раздражающим.Во-первых, нам нужно создать еще один тип предложения - first-complex-clause или что-то подобное, а во-вторых, нам нужно выписать требование, чтобы присутствовал хотя бы один параметр.

КакБолее простая альтернатива, мы можем просто потребовать, чтобы clause-list имел как минимум два clause с, а не только один.Поскольку каждый clause может также соответствовать complex-clause, ограничение на clause-list не будет отклонять любое действительное clause.Так что это дает нам очень незначительное изменение:

constant-body  : complex-clause | clause-list
clause-list    : clause ',' clause | clause-list ',' clause

Приложение

С более точным описанием предполагаемого синтаксиса (хотя в нем все еще отсутствуют некоторые детали), я изменилвыше грамматики, чтобы попытаться разобрать полный язык.Основной принцип остается неизменным: определение состоит из одного complex-clause (который включает в себя случай простого предложения) или списка по крайней мере из двух простых clause s.Единственным отличием является способ определения двух типов предложений.

Я также исправил создание списка имен, чтобы отдельные имена можно было опустить (например, (bad_block,bad_data,,,,overlay,rewrite)).Как указано в руководстве, список должен содержать хотя бы одно имя, что немного усложняет грамматику.

Я добавил опции из руководства (но не дополнительные в EBNF).Я не пытался иметь дело с пропущенными точками с запятой, которые, как представляется, недопустимы в руководстве, хотя есть пример объявления без конечной точки с запятой.(Это может быть опечатка.) Я добавил определение expression, насколько я понимаю.

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

Вот грамматика:

definition      : %empty
                | definition constant-defn
                | definition local-assign
local-assign    : LOCAL_NAME '=' expression ';'
constant-defn   : "CONSTANT" constant-body ';'
constant-body   : complex-clause | clause-list
clause-list     : clause ',' clause | clause-list ',' clause
clause          : name "EQUALS" expression
                | name-list "EQUALS" expression
complex-clause  : name "EQUALS" expression numeric-options
                | name-list "EQUALS" expression list-options
                | name "EQUALS" "STRING" string-literal string-options
                | name-list "EQUALS" "STRING" string-literal string-options
name-list       : '(' names ')'
names           : optional-commas name | names ',' name | names ','
optional-commas : %empty | optional-commas ',' 
string-options  : prefix-option tag-option
numeric-options : string-options counter-option typename-option
list-options    : increment-option numeric-options
increment-option: %empty | "INCREMENT" expression
prefix-option   : %empty | "PREFIX" prefix-name
tag-option      : %empty | "TAG" tag-name
counter-option  : %empty | "COUNTER" LOCAL_NAME
typename-option : %empty | "TYPENAME" name

expression      : '-' expression %prec UNOP
                | expression '*' expression
                | expression '/' expression
                | expression '+' expression
                | expression '-' expression
                | expression '@' expression
                | expression '&' expression
                | expression '!' expression
                | '(' expression ')'
                | INTEGER
                | IDENT
                | LOCAL_NAME
name            : IDENT | QUOTED_IDENT
prefix-name     : IDENT | QUOTED_NULL | QUOTED_IDENT
tag-name        : IDENT | QUOTED_NULL | QUOTED_IDENT | QUOTED_TAG
string-literal  : QUOTED_NULL | QUOTED_IDENT | QUOTED_TAG | QUOTED_STRING

Вы увидите, что я добавилразличение различных типов строк в кавычках, так что можно иметь дело с (большей частью) различными контекстами, в которых могут появляться строки в кавычках.Я не включил использование строк в кавычках длиной до 4 символов в качестве целочисленных констант, как указано в руководстве, потому что я не заметил этого вовремя, а также потому, что не смог выяснить с кратким чтением, является линамерение состоит в том, чтобы разрешить константы в выражениях, имена которых должны быть заключены в кавычки (потому что имя конфликтует с ключевым словом);в этом случае мне кажется, что существует двусмысленность с указанным в кавычках именем из четырех или менее символов.

В случае, если неясно, как работает дискриминация (и, вероятно, нет), вот сопутствующая информацияопределение гибкости:

id   [[:alpha:]$_][[:alnum:]$_]*

%%

[[:space:]]+         ;

CONSTANT             { return K_CONSTANT; }
COUNTER              { return K_COUNTER; }
EQUALS               { return K_EQUALS; }
INCREMENT            { return K_INCREMENT; }
PREFIX               { return K_PREFIX; }
STRING               { return K_STRING; }
TAG                  { return K_TAG; }
TYPENAME             { return K_TYPENAME; }

[#]{id}              { yylval.str = strndup(yytext, yyleng); return LOCAL_NAME; }
{id}                 { yylval.str = strndup(yytext, yyleng); return IDENT; }
["]{id}["]           { yylval.str = strndup(yytext+1, yyleng-2); return QUOTED_IDENT; }

["]["]               { yylval.str = strndup(yytext+1, yyleng-2); return QUOTED_NULL; }
["][[:alnum:]$_]+["] { yylval.str = strndup(yytext+1, yyleng-2); return QUOTED_TAG; }
["][[:print:]]+["]   { yylval.str = strndup(yytext+1, yyleng-2); return QUOTED_STRING; }

[[:digit:]]+         { yylval.dec = strtoul(yytext, 0, 0); return INTEGER; }
.                    { return *yytext; }

Я провел довольно неадекватный тест, запустив его с определениями в конце OP (хотя я добавил трейлинг ; во второй строке).На самом деле я не проверял, что дерево разбора было правильным, но оно определенно проходит через парсер.

...