【发布时间】:2018-08-18 16:00:26
【问题描述】:
我正在尝试利用 JavaScript 和 PEG.JS 将 lambda 演算术语解析为 AST。语法相当简单:
/*****************************************************************
t ::=
x variable
λx.t abstraction
t t application
*****************************************************************/
我从中编码出 PEG:
TERM "term"
= ABSTRACTION
/ APPLICATION
/ VARIABLE
APPLICATION "application"
/*****************************************************************
application ::= t t
*****************************************************************/
= APPLICATION_W_PARENS
/ APPLICATION_WO_PARENS
ABSTRACTION "abstraction"
/*****************************************************************
abstraction ::= λx.t
*****************************************************************/
= ABSTRACTION_W_PARENS
/ ABSTRACTION_WO_PARENS
VARIABLE "variable"
/*****************************************************************
variable ::= x
*****************************************************************/
= x:CHARACTER
{
return Variable(location(), x)
}
//////////////////////////////////////////////////////////////////////
// Application
//////////////////////////////////////////////////////////////////////
ABSTRACTION_OR_VARIABLE
//////////////////////////////////////////////////////////////////
// "Left recursive grammar" workaround "term term" enters a loop
// assuming the left side cannot match Application
// remediates the left recursion issue
//////////////////////////////////////////////////////////////////
= ABSTRACTION / VARIABLE
APPLICATION_W_PARENS
/*****************************************************************
'(' -> Abstraction | Variable -> Term -> ')'
*****************************************************************/
= L_PARENS lhs:ABSTRACTION_OR_VARIABLE rhs:TERM R_PARENS
{
return Application(location(), lhs, rhs, true)
}
APPLICATION_WO_PARENS
/*****************************************************************
Abstraction | Variable -> Term
*****************************************************************/
= lhs:ABSTRACTION_OR_VARIABLE rhs:TERM
{
return Application(location(), lhs, rhs, false)
}
//////////////////////////////////////////////////////////////////////
// Abstraction
//////////////////////////////////////////////////////////////////////
ABSTRACTION_W_PARENS "abstraction"
/*****************************************************************
'(' -> 'λ' -> Variable -> '.' -> TERM -> ')'
*****************************************************************/
= L_PARENS LAMBDA x:CHARACTER DOT term:TERM R_PARENS
{
return Abstraction(location(), x, term, true)
}
ABSTRACTION_WO_PARENS
/*****************************************************************
'λ' -> Variable -> '.' -> Term
*****************************************************************/
= LAMBDA x:CHARACTER DOT term:TERM
{
return Abstraction(location(), x, term, false)
}
//////////////////////////////////////////////////////////////////////
// Atoms
//////////////////////////////////////////////////////////////////////
LAMBDA "lambda"
= 'λ'
L_PARENS "lParens"
= '('
R_PARENS "rParens"
= ')'
DOT "dot"
= [\.]
CHARACTER "character"
= [A-Za-z]
{
return text().trim() ;
}
这在简单的输入上编译并运行良好。当我开始通过示例来测试实现时,我发现了一些问题。给定术语
λl.λm.λn.lmn
解析成
{
"expr": "λl.λm.λn.lmn",
"ast": " Abstraction( l, Abstraction( m, Abstraction( n, Application( Variable( l ), Application( Variable( m ), Variable( n ) ) ) ) ) )"
}
问题出在 Left Application m 应该应用于 l,然后 n 应用于该结果。正如您从 AST 的打印输出中看到的那样,n 应用于 m,而该结果应用于不正确的 l。
如果我更改了现有规则以防止左递归问题,其中应用程序假设左侧只是一个变量或抽象以包含应用程序的可能性 - 那么就会出现递归问题。
我引入了括号的概念 - 但我停止将它们集成。我真的不希望它们出现在语法中。
- 我们可以在 PEG.JS 中解决这个问题吗?
- 或者我应该重写应用程序对象的构造(hack)吗?
- 或者有没有更好的方法来解析这个 - 例如滚动自定义解析器?
【问题讨论】:
标签: javascript node.js lambda-calculus peg left-recursion