6

所以我在玩lpeg来代替boost Spirit语法,我必须说boost::spirit比lpeg优雅自然得多。然而,由于当前 C++ 编译器技术的限制和 C++ 中的 TMP 问题,它是一个婊子。在这种情况下,类型机制是你的敌人而不是你的朋友。另一方面,Lpeg 虽然丑陋和基本导致更高的生产力。

无论如何,我离题了,我的 lpeg 语法的一部分如下所示:

function get_namespace_parser()
  local P, R, S, C, V =
    lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.V

namespace_parser = 
lpeg.P{
    "NAMESPACE";
    NAMESPACE   = V("WS") * P("namespace") * V("SPACE_WS") * V("NAMESPACE_IDENTIFIER") 
                  * V("WS") * V("NAMESPACE_BODY") * V("WS"),

    NAMESPACE_IDENTIFIER = V("IDENTIFIER") / print_string ,
    NAMESPACE_BODY =  "{" * V("WS") *   
                      V("ENTRIES")^0 * V("WS") * "}",


    WS = S(" \t\n")^0,
    SPACE_WS = P(" ") * V("WS") 
}
  return namespace_parser
end 

此语法(虽然不完整)与以下内容匹配namespace foo {}。我想实现以下语义(这是使用 boost spirit 时的常见用例)。

  1. 为命名空间规则创建一个局部变量。
  2. namespace IDENTIFIER {当已匹配时,将命名空间数据结构添加到此局部变量。
  3. 将新创建的命名空间数据结构传递给NAMESPACE_BODYAST,以进一步构建 AST... 以此类推。

我确信这个用例是可以实现的。没有例子表明它。我对语言或库的了解不足以弄清楚如何去做。有人可以显示它的语法。

编辑:在尝试了几天与 lpeg 共舞并开始踩脚之后,我决定回归精神 :D 很明显 lpeg 是用 lua 函数编织的,而且这种编织是非常自由的-form(而精神有明确的非常有据可查的语义)。我只是还没有正确的 lua 心智模型。

4

1 回答 1

1

虽然“为命名空间规则创建一个局部变量”听起来令人不安,但它与 LPEG 并不真正适用的“上下文相关语法”相似,但我会假设您想要构建一个抽象语法树。

在 Lua 中,一个 AST 可以表示为一个嵌套(带有命名和索引字段)或一个闭包,执行该树应该执行的任何任务。

两者都可以通过嵌套 LPEG捕获的组合产生。

我会将这个答案限制为 AST 作为 Lua 表。

最有用的是,在这种情况下,LPEG 捕获将是:

  • lpeg.C( pattern )-- 简单的捕获,
  • lpeg.Ct( pattern )-- 表格捕获,
  • lpeg.Cg( pattern, name )-- 命名组捕获。

以下基于您的代码的示例将生成一个简单的语法树作为 Lua 表:

local lpeg = require'lpeg'
local P, V = lpeg.P, lpeg.V
local C, Ct, Cg = lpeg.C, lpeg.Ct, lpeg.Cg
local locale = lpeg.locale()
local blank = locale.space ^ 0
local space = P' ' * blank
local id = P'_' ^ 0 * locale.alpha * (locale.alnum + '_') ^ 0

local NS = P{ 'ns',
                  -- The upper level table with two fields: 'id' and 'entries':
    ns          = Ct( blank * 'namespace' * space * Cg( V'ns_id', 'id' )
                    * blank * Cg( V'ns_body', 'entries' ) * blank ),
    ns_id       = id,
    ns_body     = P'{' * blank
                         -- The field 'entries' is, in turn, an indexed table:
                       * Ct( (C( V'ns_entry' )
                       * (blank * P',' * blank * C( V'ns_entry') ) ^ 0) ^ -1 )
                       * blank * P'}',
    ns_entry    = id
}
  • lpeg.match( NS, 'namespace foo {}' )会给:
    table#1 {
        ["entries"] = table#2 {
        },
       ["id"] = "foo",
    }
    
  • lpeg.match( NS, 'namespace foo {AA}' )会给:
    table#1 {
        ["entries"] = table#2 {
            "AA"
        },
       ["id"] = "foo",
    }
    
  • lpeg.match( NS, 'namespace foo {AA, _BB}' )会给:
    table#1 {
        ["entries"] = table#2 {
            "AA",
            "_BB"
        },
       ["id"] = "foo",
    }
    
  • lpeg.match( NS, 'namespace foo {AA, _BB, CC1}' )会给:
    table#1 {
        ["entries"] = table#2 {
            "AA",
            "_BB",
            "CC1"
        },
       ["id"] = "foo",
    }
    
于 2020-09-22T00:23:33.150 回答