我正在重做我最初在Perl上构建的小语言(请参阅Chessa# on 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
这样的函数被命名为。
答案 0 :(得分:3)
在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
完整的工作示例是here。至于编程语言,有一个C parser based on Marpa。
希望这有帮助。
答案 1 :(得分:1)
您的示例存在的问题是,Grako默认启用了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')
Bitbucket repository中的Grako提示版本为生成的解析器添加了--no-nameguard
命令行选项。