Использование LPEG (грамматики выражений Lua Parser), например boost::spirit

Так что я играю с lpeg, чтобы заменить грамматику Boost Spirit, я должен сказать, что Boost:: Spirit намного элегантнее и естественнее, чем Lpeg. Однако работать с этим из-за ограничений современной технологии компилятора C++ и проблем TMP в C++ довольно сложно. Механизм типов в этом случае является вашим врагом, а не вашим другом. 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_BODY для дальнейшего строительства АСТ... и так далее, и тому подобное.

Я уверен, что этот вариант использования достижим. Нет примеров, показывающих это. Я недостаточно знаю язык или библиотеку, чтобы понять, как это сделать. Может кто-то показать синтаксис для этого.

правка: после нескольких дней попыток потанцевать с lpeg и надеть мои ноги на ноги, я решил вернуться к духу:D становится ясно, что lpeg предназначен для плетения с использованием функций lua и что такое плетение очень свободно -форма (тогда как дух имеет четкую, очень хорошо документированную семантику). У меня просто пока нет правильной ментальной модели Луа.

0 ответов

Хотя "Создать локальную переменную для правила пространства имен" тревожно звучит как "контекстно-зависимая грамматика", которая на самом деле не подходит для 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",
    }
    
Другие вопросы по тегам