2

我正在尝试用 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 代表指令/语句,CondInstrList 代表条件指令列表,CONTROLD 代表控制短划线 (~)。(CONOP 和 CONCL 表示条件打开/关闭)

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

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

因此,“else”部分与内部“if”相关联。

我只是不知道如何编写一个尊重我想要的语言方式的语法。

任何帮助表示赞赏。

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

4

1 回答 1

1

我认为您无法仅通过语法来完成您的想法。但是可以使用稍微不同的语法和词法分析器的一些帮助。

要做的事情是:不要将 ~ 标记视为单独的语法符号,而是让词法分析器将行开头的 ~ 序列转换为 INDENT 和 OUTDENT 标记,它们在您的语法中的工作方式与 { 和 } 的工作方式相同爪哇。您跟踪从零开始的“当前缩进级别”。在每一行的开头,计算 ~ 字符。对于每一个超过当前缩进级别的~,生成一个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 语言做同样的事情,但只有空格而不是 ~。如果您有兴趣,可以在这里下载 Python 源代码。查找文件Grammar\GrammarParser\tokenizer.c.

于 2017-05-17T04:21:46.363 回答