Compiler construction 传输/代码生成-变量声明问题

Compiler construction 传输/代码生成-变量声明问题,compiler-construction,antlr,abstract-syntax-tree,transpiler,implicit-declaration,Compiler Construction,Antlr,Abstract Syntax Tree,Transpiler,Implicit Declaration,我最近一直在研究ANTLR和Java,我构建了一个简单的语法来解析这些代码并生成AST。我还编写了一个内置的解释器来执行这段代码,它似乎工作得很好: 关于我的玩具语言的一些注释: 我的语言只有一种“double” 所有变量都在赋值时隐式声明 所有变量都具有全局范围。也就是说,我可以在分配变量后使用变量,即使在分配该变量的块之外 /*示例程序*/ 开始 j:=1; 而在一般情况下,在分配前检测使用是不可能的。考虑以下(不是很好)C代码: int-sum;/*未初始化*/ 对于(i=0;i

我最近一直在研究ANTLR和Java,我构建了一个简单的语法来解析这些代码并生成AST。我还编写了一个内置的解释器来执行这段代码,它似乎工作得很好:

关于我的玩具语言的一些注释:

  • 我的语言只有一种“double”
  • 所有变量都在赋值时隐式声明
  • 所有变量都具有全局范围。也就是说,我可以在分配变量后使用变量,即使在分配该变量的块之外
/*示例程序*/
开始
j:=1;

而在一般情况下,在分配前检测使用是不可能的。考虑以下(不是很好)C代码:

int-sum;/*未初始化*/
对于(i=0;i
如果
check(i)
是,比如说,
i%10==0
,那么肯定会初始化
sum
。但是如果它是
i%10==1
,则在第一次迭代中未初始化地使用
sum
。通常,是否未初始化地使用
sum
,取决于
检查(0)
的值。但可能没有办法知道那是什么<代码>检查()
可能是一个外部函数。或者其返回值可能取决于输入。或者它可能是基于一个困难的计算

这并不意味着你不应该试图发现问题。例如,您可以使用来尝试计算未定义用途的保守估计。如果可以证明未定义的用法,则可以抛出异常;如果无法证明所有用法都已定义,则可以发出警告。(许多编译器使用这种技术的变体。)这在控制流分析中可能是一个有趣的练习


但对于现实世界的解决方案,鉴于所有变量都是数字,我建议将所有变量自动初始化为0,作为语言语义的一部分。

在一般情况下,在赋值之前检测使用是不可能的。考虑以下(不是很好)C代码:

int-sum;/*未初始化*/
对于(i=0;i
如果
check(i)
是,比如说,
i%10==0
,那么肯定会初始化
sum
。但是如果它是
i%10==1
,则在第一次迭代中未初始化地使用
sum
。通常,是否未初始化地使用
sum
,取决于
检查(0)
的值。但可能没有办法知道那是什么<代码>检查()
可能是一个外部函数。或者其返回值可能取决于输入。或者它可能是基于一个困难的计算

这并不意味着你不应该试图发现问题。例如,您可以使用来尝试计算未定义用途的保守估计。如果可以证明未定义的用法,则可以抛出异常;如果无法证明所有用法都已定义,则可以发出警告。(许多编译器使用这种技术的变体。)这在控制流分析中可能是一个有趣的练习


但对于现实世界的解决方案,考虑到所有变量都是数字,我建议将所有变量自动初始化为0,作为语言语义的一部分。

我已经解决了这个问题,从各个赋值节点中删除变量声明,只需将赋值节点中使用的变量添加到全局hashmap中,然后在遍历树后生成声明

它的工作原理是这样的:

  • 沿着小路走。如果我遇到变量的用法(在赋值/输入语句中除外),则生成异常/错误
  • 如果我遇到赋值/输入语句,请将变量添加到全局hashmap中,但不要在特定节点的代码生成中声明它
  • 生成整个代码后,运行全局hashmap并生成声明
  • 通过连接声明语句和生成的代码来组合主程序
但我意识到,这可能会导致潜在的问题,变量可能在IF块内部初始化并在外部使用。如果程序已执行IF块,则没有问题,但如果跳过IF块,则我的解释器中会出现异常,但C中的代码生成仍能正常工作。但是,C p中的输出如果未执行if块,则rogram是未初始化的变量

例如(在我的代码中)

开始
i:=输入;
如果我<10,那么
j:=MUL i,10;
恩迪夫
打印j;
结束
它吐出了这个C代码(美化)

#包括
#包括
int main(int argc,char*argv[])
{
双i;
双j;
scanf(“%lf”、&i);
如果(i<10.0)
{
j=i*10.0000;
}
printf(“%g\n”,j);
}
在这种情况下,如果未到达并编译
if
块,即
i>=10
(因为
j
将保持未初始化状态),我的内置解释器将抛出异常但是,等效的C代码是正确生成和编译的,但是
j
将是一个未初始化的变量,导致运行时行为

但现在,我认为我可以接受这种行为,因为在任何情况下,使用潜在的未初始化变量都是程序本身设计的问题


我认为另一种选择是使用隐式NULL(或NaN)初始化变量值,并检查它。

我已经解决了这个问题,从各个赋值节点中删除变量声明,只需将赋值节点中使用的变量添加到全局hashmap中,然后在遍历树后生成声明

这有点管用,李
/* A sample program */
BEGIN
    j := 1;
    WHILE j <= 5 DO
        PRINT "ITERATION NO: "; PRINTLN j;
        sumA1 := 0;
        WHILE 1 = 1 DO 
            PRINT "Enter a number, 0 to quit: ";
            i := INPUT;
            IF i = 0 THEN
                BREAK;
            ENDIF
            sumA1 := ADD sumA1, i;
        ENDWHILE
        j := ADD j, 1;
        PRINT "The sum is: "; PRINTLN sumA1;
    ENDWHILE
    j := MINUS j;
    PRINTLN j;
END
#include <stdio.h>

#include <stdlib.h>

int main(int argc, char * argv[]) {
  double j;
  j = 1.00000;
  while (j <= 5.0) {
    printf("ITERATION NO: ");
    printf("%g\n", j);
    double sumA1;
    sumA1 = 0.00000;
    while (1.0 == 1.0) {
      printf("Enter a number, 0 to quit: ");
      double i;
      scanf("%lf", & i);
      if (i == 0.0) {
        break;
      }
      sumA1 = sumA1 + i;
    }
    j = j + 1.00000;
    printf("The sum is: ");
    printf("%g\n", sumA1);
  }
  j = -j;
  printf("%g\n", j);
}
int sum;          /* uninitialised */
for (i = 0; i < n; ++i) {
  if (check(i)) sum = 0;
  sum += val[i];  /* Is sum initialised here? */
  process(sum);
}
BEGIN
   i := INPUT;
   IF i < 10 THEN
      j := MUL i, 10;
   ENDIF
   PRINT j;
END
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
   double i;
   double j;
   scanf("%lf", &i);
   if (i < 10.0)
   {
      j = i *10.0000;
   }
   printf("%g\n", j);
}