lpeg разобрать логический термин первого порядка
Как видно из названия, я пытаюсь разобрать например
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"
и порядок этих элементов не фиксирован). Как я могу пометить элементы, поддерживающие порядок?
2 ответа
В вашей грамматике у вас есть:
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"
}
Внимательно изучив вышесказанное, вы заметите, что аргументы функции появляются в индексной части таблицы в том порядке, в котором они были переданы. 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()
преобразованная строка для создания таблицы.