【问题标题】:Assignment grammar conflicting with λ-calculus application grammar赋值文法与 λ-演算应用文法冲突
【发布时间】:2020-08-27 23:24:01
【问题描述】:

我正在使用 ANTLR4 实现扩展的 λ-演算解释器,它是 C++ 目标。这是语言语法:

grammar lambda;

program: expression|;

expression:
    (Int | Bool)                                # literal
    | Identifier                                # variable
    | expression expression                     # application
    | Lambda Identifier '.' expression          # abstraction
    | Identifier '=' expression                 # assign
    | condition                                 # conditional
    | Operator expression expression            # binaryExpression
    | 'print' expression                        # printInstruction
    | '(' expression ')'                        # brackets;

body: expression;
condition: 'if' expression 'then' body 'else' body
    | '(' expression '->' body '|' body;

Lambda: '\\' | 'λ';
Bool : 'tru' | 'fls' | 'true' | 'false';
Int: [0-9]+;
Identifier: ('a' ..'z') ('a' ..'z' | '0' ..'9')*;
Operator:
    '+'
    | '-'
    | '*'
    | '/'
    | '<'
    | '>'
    | '<='
    | '>='
    | '==';

WS: [ \n\t\r]+ -> skip;

我正在使用访问者模型构建一个 AST,该模型将被单独评估。我在 ANTLR 解析输入的方式上遇到了问题,我什至不确定该怎么称呼它。

问题 1

// incorrect_association.lambda

y = 1
x = 1

Assignment ( y = ( Application ( Literal ( 1 ) ) ( Assignment ( x = ( Literal ( 1 ) ) ) ) ) )

AST 应该是

Assignment ( y = ( Literal ( 1 ) )
Assignment ( x = ( Literal ( 1 ) )

Grouping (
    Assignment ( y = ( Literal ( 1 ) ),
    Assignment ( x = ( Literal ( 1 ) )
)

问题 2

我想这可能与第一个问题有关:跨多行的表达式被读取为 Application 表达式。

// incorrect_application.lambda

x = 1
print x

Assignment ( x = ( Application ( Literal ( 1 ) ) ( PrintInstruction ( Identifier ( "x" ) ) ) ) )

AST 应该是

Assignment ( x = ( Literal ( 1 ) )
PrintInstruction ( Identifier ( "x" ) )

Grouping (
    Assignment ( x = ( Literal ( 1 ) ),
    PrintInstruction ( Identifier ( "x" ) )
)

我正在尝试进行类似命令式的常量赋值,以及类似函数式的执行。最终,该程序应该是 main = ...(如 Haskell)。是否可以阻止 Application 规则匹配位于不同行的两个表达式,但继续允许任何其他空格和括号?

可能的解决方案

我正在考虑编写一个预处理器,它只会在每一行的结尾加上分号。无论如何我可能需要这样做,因为我打算添加

imports: 'import' Identifier | '(' imports ')';

作为语法规则,并没有找到一个很好的解决方案来处理使用 ANTLR 的导入。如果我要走这条路,我将如何在我的语法中包含 ; 行尾?

PS:我是 ANTLR 的新手,所以任何指导都会非常有帮助。

【问题讨论】:

    标签: c++ parsing antlr grammar lambda-calculus


    【解决方案1】:

    如果您希望换行符有意义,则让它们通过词法扫描器。

    WS: [ \t\r]+ -> skip;
    NL: [\n];
    

    然后您可以将程序定义为以换行符结尾的表达式序列:

    program: ( expression NL )*;
    

    如果您希望分号也能正常工作,只需更改 NL 的定义:

    NL: [\n;];
    

    您还需要更改body 以接受多个表达式,尽管我不清楚您要使用哪种标点符号。有可能

    body: expression (NL expression)*;
    

    对你有用,但可能会产生意想不到的结果。

    您的应用程序语法非常模糊。我不知道 Antlr 会怎么做,但我无法解释它。如果你有

    + a b c
    

    必须是以下之一:

    (+ a b) (c)
    (+ a (b c))
    (+ (a b) c)
    

    但我没有看到任何迹象表明应该首选这三个中的哪一个。我认为您需要提出一种具有更精确优先级的语法。

    (Lisp 和 Scheme 使用括号是有原因的 :-))

    【讨论】:

    • 感谢您的回复! λ-演算是左结合的,所以(+ a (b c)) 是首选。我认为添加NL 规则就可以了!我会尝试并回复你:)
    • @Iain:左关联将是((+ a b) c),因为我理解关联的含义(尽管在这种情况下应用有点困难。)b c 被解析为(b c)需要优先使用应用程序的右关联。
    • 你是对的:D。我最近在大脑上进行了教堂编码。
    • 我使用你的建议让它工作了,但是如果从那时起有一个空行,我的访问者不会被调用。有一个简单的解决方法吗?还是需要提出一个新问题?
    • @Iain:我在心里记下要提到空行的问题,然后我就忘记了。 ( expression? NL )*( expression NL+ )* 中的任何一个都应该可以工作。
    猜你喜欢
    • 2021-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 2017-01-06
    • 1970-01-01
    • 2013-06-23
    相关资源
    最近更新 更多