如何定义对嵌套 "if" 指令使用多个破折号的语法?

How to define a syntax that uses multiple dashes for nested "if" instructions?

我正在尝试在 Java(使用 CUP)中创建一个可以识别这段代码的语法分析器:

if ¿b? then
~ a = 2;
~ if ¿b && c? then
~ ~ a = 3;
else
~ a = 4;

"if" 语句使用的我的作品如下:

Instr ::= ...
       | IF CONOP Exp:e CONCL THEN CondInstrList:l
       ...
       ;
...
CondInstrList ::= CondInstrList CondInstr
       | /*empty*/
       ;
...
CondInstr ::= CONTROLD Instr
       | CONTROLD CondInstr
       ;

其中 Instr 代表 instruction/statement,CondInstrList 代表条件指令列表,CONTROLD 代表控制破折号 (~)。 (CONOP 和 CONCL 表示条件 Open/Close)

问题在于使用该语法,生成的 AST 如下:

if
|-condition b
|-condInstrListT
  |---asig a = 2
  |---if
      |---condition b and c
      |---condInstrListT 
      |   |---asig a = 2
      |---condInstrListF
          |---asig a = 4

因此,"else" 部分与内部 "if".

相关联

我只是不知道如何编写符合我希望我的语言的语法。

感谢任何帮助。

如果需要,我可以提供更多细节。

我认为单靠语法是无法做到你心中所想的。但是使用稍微不同的语法和词法分析器的一些帮助是可能的。

要做的是:与其将 ~ 标记视为单独的语法符号,不如让词法分析器将行首的 ~ 序列转换为 INDENT 和 OUTDENT 标记,它们将在您的语法中以与{ 和 } 在 Java 中工作。您跟踪从零开始的 "current indent level"。在每一行的开头,计算 ~ 字符。对于超过当前缩进级别的每个 ~,生成一个 INDENT 标记并增加当前缩进级别;对于每个小于当前缩进级别的 ~,生成一个 OUTDENT 标记并降低当前缩进级别。

所以你的示例文本

if ¿b? then
~ a = 2;
~ if ¿b && c? then
~ ~ a = 3;
else
~ a = 4;

将被标记为:

// Indent level = 0 and no ~, so no INDENT here
[IF] [CONOP] [ID b] [CONCL] [THEN]
// Indent level = 0, one ~, so one INDENT
[INDENT]
    // Indent level = 1
    [ID a] [OP =] [CONST 2] [SEMICOLON]
    // Indent level = 1, one ~, so no INDENT here
    [IF] [CONOP] [ID b] [OP &&] [ID c] [CONCL] [THEN]
    // Indent level = 1, two ~, so one INDENT
    [INDENT]
        // Indent level = 2
        [ID a] [ASSIGN] [CONST 3] [SEMICOLON]
        // Indent level = 2, lines starts with no ~, two OUTDENTs
    [OUTDENT]
    // Indent level = 1
[OUTDENT]
//Indent level = 0
[ELSE] // No ~ at start of this line, so no INDENT
// Indent level = 0; one ~, so one INDENT
[INDENT] 
    // Indent level = 1
    [ID a] [ASSIGN] [CONST 4] [SEMICOLON]
// End-of-input.  Indent level = 1, so 1 OUTDENT
[OUTDENT]
// Done; indent level = 0;

INDENT 和 OUTDENT 标记在您的语法中的作用类似于 Java 中的左右大括号,因此您的语法可能类似于:

Instr ::= ...
       | IF CONOP Exp:e CONCL THEN INDENT CondInstrList:l OUTDENT
       ...
       ;
...
CondInstrList ::= CondInstrList Instr
       | /*empty*/
       ;
...

语言 Python 做同样的事情,但只有白色 space 而不是 ~。如果您有兴趣,可以下载 Python 来源 here。查找文件 Grammar\GrammarParser\tokenizer.c.