正如标题所说,我知道lua在Lua的完全句法中有一个官方的扩展BNF。我想写一个聚乙二醇传递给lpeg.re.compile来解析lua本身。也许Lua有点像BNF。我读过BNF并试图把它翻译成PEG,但是我发现数字和LiteralString很难写。有人做过这样的事吗?
local lpeg = require "lpeg"
local re = lpeg.re
local p = re.compile([[
chunk <- block
block <- stat * retstat ?
stat <- ';' /
varlist '=' explist /
functioncall /
label /
'break' /
'goto' Name /
'do' block 'end' /
'while' exp 'do' block 'end' /
'repeat' block 'until' exp /
'if' exp 'then' block ('elseif' exp 'then' block) * ('else' block) ? 'end' /
'for' Name '=' exp ',' exp (',' exp) ? 'do' block 'end' /
'for' namelist 'in' explist 'do' block 'end' /
'function' funcname funcbody /
'local function' Name funcbody /
'local' attnamelist ('=' explist) ?
attnamelist <- Name attrib (',' Name attrib) *
attrib <- ('<' Name '>') ?
retstat <- 'return' explist ? ';' ?
label <- '::' Name '::'
funcname <- Name ('.' Name) * (':' Name) ?
varlist <- var (',' var) *
var <- Name / prefixexp '[' exp ']' / prefixexp '.' Name
namelist <- Name (',' Name) *
explist <- exp (',' exp) *
exp <- 'nil' / 'false' / 'true' / Numeral / LiteralString / "..." / functiondef /
prefixexp / tableconstructor / exp binop exp / unop exp
prefixexp <- var / functioncall / '(' exp ')'
functioncall <- prefixexp args / prefixexp ":" Name args
args <- '(' explist ? ')' / tableconstructor / LiteralString
functiondef <- 'function' funcbody
funcbody <- '(' parlist ? ')' block 'end'
parlist <- namelist (',' '...') ? / '...'
tableconstructor <- '{' fieldlist ? '}'
fieldlist <- field (fieldsep field) * fieldsep ?
field <- '[' exp ']' '=' exp / Name '=' exp / exp
fieldsep <- ',' / ';'
binop <- '+' / '-' / ‘*’ / '/' / '//' / '^' / '%' /
'&' / '~' / '|' / '>>' / '<<' / '..' /
'<' / '<=' / '>' / '>=' / '==' / '~=' /
'and' / 'or'
unop <- '-' / 'not' / '#' / '~'
saveword <- "and" / "break" / "do" / "else" / "elseif" / "end" /
"false" / "for" / "function" / "goto" / "if" / "in" /
"local" / "nil" / "no"t / "or" / "repeat" / "return" /
"then" / "true" / "until" / "while"
Name <- ! saveword / name
Numeral <-
LiteralString <-
]])发布于 2022-03-30 20:50:51
首先:您需要在两个步骤中解析Lua,包括标记化(词法分析,RegEx)和解析(语法分析,CFGs)。考虑语法上无效的Lua代码if1then print()end。如果您只一次解析这一点,您可能不会得到语法错误,因为理论上它可以合理地被解释为if - number 1 - then .但是,令牌化会贪婪地使if1成为一个单一的“标识符”/name令牌,从而在后面的语法分析中触发语法错误。
在某些情况下,PEGs可能允许通过他们的有序选择来表达这一点,但是通常情况下,应该应用两步过程,以避免获得过于宽松的语法(也可能是模糊的语法)。
仍有待编写的“规则”都是令牌规则(从大写名称中可以看出)-- Name、LiteralString和Numeral。这些基本上只是简单的RegExes。至于Names:如果您巧妙地使用PEGs的有序选择+,则不必使用“减法”(负前瞻性)来避免关键字被解析为Names:只需在标记化语法中按照Token = Keyword + Name + ...的方式做一些事情。
文字字符串确实很棘手,因为长字符串不能以RegExes的形式编写;引用的字符串相当容易(但您必须处理转义)。LPeg文档有一个关于长字符串的示例:
equals = lpeg.P"="^0
open = "[" * lpeg.Cg(equals, "init") * "[" * lpeg.P"\n"^-1
close = "]" * lpeg.C(equals) * "]"
closeeq = lpeg.Cmt(close * lpeg.Cb("init"), function (s, i, a, b) return a == b end)
string = open * lpeg.C((lpeg.P(1) - closeeq)^0) * close / 1数字有点笨重,因为你必须处理许多不同的情况,对不同的基,省略的点,省略0之前或之后的点,指数,符号等。
我碰巧有相关的LPeg规则,这些规则随处可见:
-- Character classes
_letter = R("AZ", "az")
_letter_ = _letter + P"_"
_digit = R"09"
_hexdigit = _digit + R("AF", "af")
white = C(S" \f\t\v\n\r" ^ 1)
_keyword = P"not"
+ P"and"
+ P"or"
+ P"function"
+ P"nil"
+ P"false"
+ P"true"
+ P"return"
+ P"goto"
+ P"do"
+ P"end"
+ P"while"
+ P"repeat"
+ P"until"
+ P"if"
+ P"then"
+ P"elseif"
+ P"else"
+ P"for"
+ P"local"
-- Names
Name = C(_letter_ * (_letter_ + _digit) ^ 0) - _keyword
-- Numbers
local function _numeral(digit_, exponent_letter)
local uint = digit_ ^ 1
local float = uint + uint * P"." * uint + uint * P"." + P"." * uint
local exponent = exponent_letter * O(S"+-") * uint
return C(float) * C(O(exponent))
end
_hex_numeral = C(P"0x") * _numeral(_hexdigit, S"pP")
_decimal_numeral = _numeral(_digit, S"eE")
Numeral = _hex_numeral + _decimal_numeral
-- Strings
decimal_escape = C(_digit * O(_digit * O(_digit)))
hex_escape = P"x" * C(_hexdigit * _hexdigit)
unicode_escape = P"u{" * C(_hexdigit^1) * P"}"
char_escape = C(S[[abfnrtv\'"]])
_escape = P[[\]] * (decimal_escape + hex_escape + char_escape + unicode_escape)
local function string_quoted(quotes)
local range = P(1) - S(quotes .. "\0\n\r\\")
local content = (_escape + C(range^1)) ^ 0
return Cg(P(quotes), "quotes") * content * P(quotes)
end
local equals = P"=" ^ 0
local open = P"[" * Cg(equals, "equals") * P"[" * O(P"\n")
local close = Cmt(P"]" * C(equals) * P"]" * Cb"equals", function(_, _, open, close)
return open == close
end)
_long_string = open * C((P(1) - close) ^ 0) * close
String = string_quoted[[']] + string_quoted[["]] + _long_string
_line_comment = -open * ((P(1) - P"\n") ^ 0) * (P"\n" + _eof)
Comment = P"--" * (_long_string + _line_comment)除非在自定义环境中加载规则,否则您可能希望本地化这些变量中的一些(如果不是全部)。
https://stackoverflow.com/questions/71673198
复制相似问题