4

正如标题所说,我正在尝试解析例如

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。有没有办法避免这种情况?
  • 我想有一个与每个元素关联的键来指定它的类型,例如,而不是A{value = "A", type = "variable"}. 我找到了一种方法来做到这一点,{:name: :}但是,表中元素的顺序丢失了(因为它不会创建一个新表,而只是添加一个键,在这种情况下variable="A",这个元素的顺序是不固定的)。如何标记维持订单的物品?
4

2 回答 2

6

在你的语法中,你有:

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 typeandname可以以任何顺序出现,因为它位于表的关联部分中。您可以将这些“属性”包装在另一个表中,并将该内部属性表放在外部表的索引部分中。

编辑:这是一个修改后的语法,使解析更加统一。我删除了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"
}
于 2013-07-26T23:00:34.303 回答
2

抱歉,我没有使用 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()转换字符串即可创建表。

于 2013-07-26T16:07:14.313 回答