Правило приоритета вопроса с грако
Я переделываю мини-язык, который я изначально построил на Perl (см. Chessa# на github), но у меня возникает ряд проблем, когда я применяю семантику.
(* integers *)
DEC = /([1-9][0-9]*|0+)/;
int = /(0b[01]+|0o[0-7]+|0x[0-9a-fA-F]+)/ | DEC;
(* floats *)
pointfloat = /([0-9]*\.[0-9]+|[0-9]+\.)/;
expfloat = /([0-9]+\.?|[0-9]*\.)[eE][+-]?[0-9]+/;
float = pointfloat | expfloat;
list = '[' @+:atom {',' @+:atom}* ']';
(* atoms *)
identifier = /[_a-zA-Z][_a-zA-Z0-9]*/;
symbol = int |
float |
identifier |
list;
(* functions *)
arglist = @+:atom {',' @+:atom}*;
function = identifier '(' [arglist] ')';
atom = function | symbol;
prec8 = '(' atom ')' | atom;
prec7 = [('+' | '-' | '~')] prec8;
prec6 = prec7 ['!'];
prec5 = [prec6 '**'] prec6;
prec4 = [prec5 ('*' | '/' | '%' | 'd')] prec5;
prec3 = [prec4 ('+' | '-')] prec4;
(* <| and >| are rotate-left and rotate-right, respectively. They assume the nearest C size. *)
prec2 = [prec3 ('<<' | '>>' | '<|' | '>|')] prec3;
prec1 = [prec2 ('&' | '|' | '^')] prec2;
expr = prec1 $;
Проблема, с которой я сталкиваюсь, заключается в том, что d
оператор втягивается в правило идентификатора, когда между оператором и любыми последующими буквенно-цифровыми строками нет пробелов. Хотя самой грамматикой является LL(2), я не понимаю, в чем здесь проблема.
Например, 4d6
останавливает анализатор, потому что он интерпретируется как 4
d6
, где d6
это идентификатор. Что должно произойти, это то, что это интерпретируется как 4
d
6
с d
будучи оператором. В LL-парсере это действительно так.
Возможное решение было бы запретить d
от начала идентификатора, но это будет запрещать такие функции, как drop
быть названным таковым.
2 ответа
Проблема с вашим примером в том, что у Грако есть nameguard
функция включена по умолчанию, и это не позволит анализировать только d
когда d6
впереди
Чтобы отключить эту функцию, создайте свою собственную Buffer
и передать его экземпляру сгенерированного парсера:
from grako.buffering import Buffer
from myparser import MyParser
# get the text
parser = MyParser()
parser.parse(Buffer(text, nameguard=False), 'expre')
Типовая версия Grako в репозитории Bitbucket добавляет --no-nameguard
опция командной строки для сгенерированных парсеров.
В Perl вы можете использовать Marpa, общий синтаксический анализатор BNF, который поддерживает обобщенный приоритет с ассоциативностью (и многие другие) из коробки, например
:start ::= Script
Script ::= Expression+ separator => comma
comma ~ [,]
Expression ::=
Number bless => primary
| '(' Expression ')' bless => paren assoc => group
|| Expression '**' Expression bless => exponentiate assoc => right
|| Expression '*' Expression bless => multiply
| Expression '/' Expression bless => divide
|| Expression '+' Expression bless => add
| Expression '-' Expression bless => subtract
Полный рабочий пример здесь. Что касается языков программирования, то есть синтаксический анализатор C, основанный на Marpa.
Надеюсь это поможет.