首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何编写用于lpeg的peg来解析lua本身?

如何编写用于lpeg的peg来解析lua本身?
EN

Stack Overflow用户
提问于 2022-03-30 07:30:06
回答 1查看 152关注 0票数 0

正如标题所说,我知道lua在Lua的完全句法中有一个官方的扩展BNF。我想写一个聚乙二醇传递给lpeg.re.compile来解析lua本身。也许Lua有点像BNF。我读过BNF并试图把它翻译成PEG,但是我发现数字和LiteralString很难写。有人做过这样的事吗?

代码语言:javascript
复制
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 <- 
]])
EN

回答 1

Stack Overflow用户

发布于 2022-03-30 20:50:51

首先:您需要在两个步骤中解析Lua,包括标记化(词法分析,RegEx)和解析(语法分析,CFGs)。考虑语法上无效的Lua代码if1then print()end。如果您只一次解析这一点,您可能不会得到语法错误,因为理论上它可以合理地被解释为if - number 1 - then .但是,令牌化会贪婪地使if1成为一个单一的“标识符”/name令牌,从而在后面的语法分析中触发语法错误。

在某些情况下,PEGs可能允许通过他们的有序选择来表达这一点,但是通常情况下,应该应用两步过程,以避免获得过于宽松的语法(也可能是模糊的语法)。

仍有待编写的“规则”都是令牌规则(从大写名称中可以看出)-- NameLiteralStringNumeral。这些基本上只是简单的RegExes。至于Names:如果您巧妙地使用PEGs的有序选择+,则不必使用“减法”(负前瞻性)来避免关键字被解析为Names:只需在标记化语法中按照Token = Keyword + Name + ...的方式做一些事情。

文字字符串确实很棘手,因为长字符串不能以RegExes的形式编写;引用的字符串相当容易(但您必须处理转义)。LPeg文档有一个关于长字符串的示例:

代码语言:javascript
复制
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规则,这些规则随处可见:

代码语言:javascript
复制
-- 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)

除非在自定义环境中加载规则,否则您可能希望本地化这些变量中的一些(如果不是全部)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71673198

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档