正如标题所说,我正在尝试解析例如
term(A, b, c(d, "e", 7))
在类似的Lua表格中
{term, {A, b, {c, {d, "e", 7}}}}
这是我建立的语法:
local pattern = re.compile[=[
term <- variable / function
argument <- variable / lowercase /number / string
function <- {|lowercase {|(open argument (separator (argument / function))* close)?|}|}
variable <- uppercase
lowercase <- {[a-z][A-Za-z0-9]*}
uppercase <- {[A-Z][A-Za-z0-9]*}
string <- '"' {~ [^"]* ~} '"'
number <- {[0-9]+}
close <- blank ")"
open <- "(" blank
separator <- blank "," blank
blank <- " "*
]=]
我遇到了以下问题:
- 它无法解析嵌套术语。对于上面的示例,它只返回
{term, {} }
(而使用term(A, b, c)
可以) - 为了去掉字符串中的引号,我使用了
{~ ~}
,但正因为如此,我不得不在下面的行中移动argument
和term
中的所有捕获。有办法避免这种情况吗 - 我希望有一个与每个元素相关联的键来指定其类型,例如,而不是像
{value = "A", type = "variable"}
这样的A
。我找到了一种使用{:name: :}
的方法,但是,表中元素的顺序丢失了(因为它不创建新表,只是添加了一个键,在这种情况下是variable="A"
,并且这些元素的顺序不是固定的)。如何标记维护订单的项目
在语法中有:
argument <- variable / lowercase /number / string
function <- {|lowercase {|(open argument (separator (argument / function))* close)?|}|}
请记住,lpeg试图按照您的顺序匹配规则中的模式/谓词。一旦找到匹配,lpeg就不会考虑该语法规则中进一步的可能匹配,即使以后可能会有"更好"的匹配。
在这里,它无法匹配嵌套的函数调用,因为它看到c
可以匹配
`argument <- variable`
由于您的variable
非终端列在function
之前,所以lpeg不考虑后者,因此它停止解析后面的令牌。
作为一个实验,我稍微修改了你的语法,并添加了一些表&为您感兴趣的大多数非终端命名捕获。
local pattern = re.compile
[=[
term <- {| {:type: '' -> "term" :} term_t |}
term_t <- func / var
func <- {| {:type: '' -> "func":} {:name: func_id:} "(" arg(separator arg)* ")" |}
func_id <- lower / upper
arg <- number / string / term_t
var <- {| {:type: '' -> "var" :} {:name: lower / upper:} |}
string <- '"' {~ [^"]* ~} '"'
lower <- {%l%w*}
upper <- {%u%w*}
number <- {%d+}
separator <- blank "," blank
blank <- " "*
]=]
快速模式测试:
local test = [[fun(A, b, c(d(42), "e", f, 7))]]
dump( pattern:match(test) )
它在我的机器上给出以下输出:
{
{
{
type = "var",
name = "A"
},
{
type = "var",
name = "b"
},
{
{
"42",
type = "func",
name = "d"
},
"e",
{
type = "var",
name = "f"
},
"7",
type = "func",
name = "c"
},
type = "func",
name = "fun"
},
type = "term"
}
仔细看上面的内容,您会注意到函数参数按传入的顺序出现在表的索引部分。OTOH type
和name
可以按任何顺序出现,因为它位于表的关联部分。您可以将这些"属性"包装在另一个表中,并将内部属性表放在外部表的索引部分。
编辑:这里有一个修改后的语法,使解析更加统一。我删除了term
捕获,以帮助修剪一些不必要的分支。
local pattern2 = re.compile
[=[
term <- term_t
term_t <- func / var
func <- {| {:type: '' -> "func":} {:name: func_id:} "(" args? ")" |}
func_id <- lower / upper
arg <- number / string / term_t
args <- arg (separator args)?
var <- {| {:type: '' -> "var" :} {:name: lower / upper:} |}
string <- {| {:type: '' -> "string" :}'"' {:value: [^"]* :} '"' |}
lower <- {%l%w*}
upper <- {%u%w*}
number <- {| {:type: '' -> "number":} {:value: %d+:} |}
separator <- blank "," blank
blank <- " "*
]=]
得出以下结果:
{
{
type = "var",
name = "A"
},
{
type = "var",
name = "b"
},
{
{
{
type = "number",
value = "42"
},
type = "func",
name = "d"
},
{
type = "string",
value = "e"
},
{
type = "var",
name = "f"
},
{
type = "number",
value = "7"
},
type = "func",
name = "c"
},
type = "func",
name = "fun"
}
对不起,我没有LPeg的经验,但通常的Lua模式足以轻松解决您的任务:
local str = 'term(A, b, c(d, "e", 7))'
local function convert(expr)
return (expr:gsub('(%w+)(%b())',
function (name, par_expr)
return '{'..name..', {'..convert(par_expr:sub(2, -2))..'}}'
end
))
end
print(convert(str)) -- {term, {A, b, {c, {d, "e", 7}}}}
现在只需要load()
转换字符串来创建一个表。