首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在保持左联想的同时避免左递归解析Lambda演算

在保持左联想的同时避免左递归解析Lambda演算
EN

Stack Overflow用户
提问于 2018-08-18 16:00:26
回答 1查看 332关注 0票数 2

我试图利用JavaScript和PEG.JS将lambda演算术语解析为AST。语法相当简单:

代码语言:javascript
复制
/*****************************************************************
        t ::=
                x                                       variable
                λx.t                                    abstraction
                t t                                     application
*****************************************************************/

我从其中编码出了PEG:

代码语言:javascript
复制
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() ;
    }

这在简单输入上编译并运行良好。当我开始通过示例测试实现时,我看到了一些问题。鉴于这一术语

代码语言:javascript
复制
λl.λm.λn.lmn

它解析成

代码语言:javascript
复制
{
    "expr": "λl.λm.λn.lmn",
    "ast": " Abstraction( l,  Abstraction( m,  Abstraction( n, Application(  Variable( l ), Application(  Variable( m ),  Variable( n ) ) ) ) ) )"
}

问题是,在左边的应用程序中,应该将m应用于l,然后将n应用于结果。从AST的打印输出可以看出,n应用于m,而结果应用于l,这是不正确的。

如果我更改了用于防止左递归问题的规则,其中应用程序假定左侧只是一个变量或一个抽象,以包含应用程序的可能性,那么就会出现递归问题。

我引入了父母的概念--但我停止了将他们整合进来。我真的不想让他们出现在语法里。

  1. 我们能在PEG.JS中解决这个问题吗?
  2. 或者我应该重写应用程序对象(hack)的构造?
  3. 或者有更好的方法来解析这个--例如滚一个自定义解析器?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-08-18 20:07:07

有缺陷的方法:我玩过的一种方法是强制在一行中匹配两个以上的变量,然后继续应用它们

代码语言:javascript
复制
APP_2
    = lhs:ABSTRACTION_OR_VARIABLE rhs:ABSTRACTION_OR_VARIABLE
    {
        return Application(location(), lhs, rhs, false, "APP2")
    }

APP_3
    = lhs:APP_2 rhs:TERM
    {
        return Application(location(), lhs, rhs, false, "APP3")
    }

APPLICATION_WO_PARENS
    = APP_3
    / APP_2

当应用程序是三个术语时,它看起来是有效的。当有四棵树时,我们得到了一棵由两层组成的平衡树,其中我们想要一棵三层的不平衡树。因此,这是前一个PEG的不正确输出(输入为lmno):

代码语言:javascript
复制
{
    "expr": "lmno",
    "ast": "Application::APP3( 
              Application::APP2(  Variable(l),  Variable(m) ),
              Application::APP2(  Variable(n),  Variable(o) ) 
            )"
}

所以我可以造出任意数量的APP_2 .APP_99规则并强制左侧应用程序。这是可行的-直到我超过了99 (或其他)的申请数量。解决方案将是一个真正的黑客和脆弱的。

Working Hack Approach:想要一起破解一些东西,我改变了将一系列术语作为应用程序进行匹配的方法:

代码语言:javascript
复制
APP_ARR
    = terms:ABSTRACTION_OR_VARIABLE*
   {
        return reduceTerms(location(), terms)
   }

APPLICATION_WO_PARENS
    = APP_ARR

这种方法要求我编写一些代码来构建我试图避免的结构(reduceTerms)。以下是代码:

代码语言:javascript
复制
const reduceTerms = function(info, terms){
    const initialLhs = terms.shift()
    const initialRhs = terms.shift()
    const initialApp = Application(info, initialLhs, initialRhs, false, 'reduceTerms')

    const appAll = terms.reduce(
        (lhs, rhs) => Application(info, lhs, rhs, false, 'reduceTerms'),
        initialApp
    )

    return appAll ;
}

请忽略布尔字符串和“reduceTerms”字符串。布尔值用于指示该应用程序是否包含在parens中(将删除parens概念,直到我在本书后面遇到它)。字符串是关于我如何/在何处构造这个应用程序节点实例的标签(用于调试解析器如何应用规则)。

reduceTerms函数是将数组简化为应用程序树,应用左侧应用程序中的术语。初始对象是术语数组中左边两个项的应用程序。减少功能将把最初的对象作为lhs,下一个术语作为rhs,这正是你想要的。以下是工作输出:

代码语言:javascript
复制
{
    "expr": "lmno",
    "ast": "Application::reduceTerms( 
              Application::reduceTerms( 
                Application::reduceTerms(  Variable(l),  Variable(m) ),  
              Variable(n) ),  
            Variable(o) )"
}

这里的一个问题是,我需要做一些额外的工作,以获得信息对象,以准确地反映匹配。在这个版本中,info对象包含匹配所有术语的范围。虽然不重要,但最好能把这一切都解决掉。

因此,我仍然在寻找一种解决方案,它可以在PEG内部完成,而不需要在数组上进行匹配,并且可以简化为树。

关于删除左递归的进展:使用已发布的方法进行翻译

代码语言:javascript
复制
A -> A α | β

代码语言:javascript
复制
A -> β A'
A' -> α A' | ε

在PEG中我有

代码语言:javascript
复制
TERM_OR_VARIABLE
    = L_PARENS TERM R_PARENS
    / VARIABLE

APP
   = lhs:TERM_OR_VARIABLE rhs:APP_
   {
       return Application(location(), lhs, rhs, false, "APP")
   }

APP_
    = lhs:TERM_OR_VARIABLE rhs:APP_
    {
        return Application(location(), lhs, rhs, false, "APP_")
    }
    / lhs:TERM_OR_VARIABLE END
    {
        return lhs
    }
    / END

END
    = !.

APPLICATION_WO_PARENS
    = APP

我现在能够解析应用程序lmno,但是AST是从右到左的应用程序。

代码语言:javascript
复制
Application::APP(  
    Variable(l), 
    Application::APP_(  
       Variable(m), 
       Application::APP_(  
          Variable(n),  
          Variable(o) 
       ) 
    ) 
)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51910361

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档