Parsing 具有void*类型的Lemon解析器标记值

Parsing 具有void*类型的Lemon解析器标记值,parsing,parameters,terminal,lemon,re2c,Parsing,Parameters,Terminal,Lemon,Re2c,我试图使用void*type作为我的lemon解析器,但我遇到了一些奇怪的问题 最初我使用一个自定义令牌类型,一个用于保存令牌值的结构,然后我切换到void*,因为我的令牌值类型不同 下面是我的一些解析器代码 expression(A) ::= expression(B) PLUS expression(C). { *((double *)A)= *((double *)B) + *((double *)C) ; } expression(A) ::= expression(B) MINU

我试图使用void*type作为我的lemon解析器,但我遇到了一些奇怪的问题

最初我使用一个自定义令牌类型,一个用于保存令牌值的结构,然后我切换到void*,因为我的令牌值类型不同

下面是我的一些解析器代码

expression(A) ::= expression(B) PLUS expression(C). { *((double *)A)=  *((double *)B)  +  *((double *)C) ; }
expression(A) ::= expression(B) MINUS expression(C). { *((double *)A)= *((double *) B)  -  *((double *)C) ;  }
expression(A) ::= expression(B) MULT expression(C). { *((double *)A)=  *((double *)B)  *   *((double *)C) ; }
expression(A) ::= expression(B) DIV expression(C). {
        if( *((double *)C)  != 0)
                *((double *)A)=  *((double *)B)  /  *((double *)C) ;
        else
                printf("Math Error!");
}

expression(A) ::= number(B). { *((double *)A)=  *((double *)B) ;}
number ::= INT.
number ::= FLOAT.
这是我的lexer,它是re2c文件

while ((token = lex()) != EOL) {
        sy[size].val = tkn.val;

        parse(parser, token, &sy[size].val);
        size++;
}
sy[size].val
是双精度类型

但是当我运行
1+2
时,它返回4,当我运行
1+4
时,它返回8


我的猜测是,解析器将最右边的值放入堆栈,并在它看到令牌参数的任何地方使用它。

下面是一个简单但错误的程序:

double* add_indirect(double* b, double* c) {
  double *a;
  *a = *b + *c;    /* Undefined behaviour! */
  return a;        /* This, too! */
}
应该清楚该程序错误的原因:
a
从未初始化过。它的声明说它是指向双精度的指针,但它从来没有指向任何东西。因此,当试图通过第3行中的指针存储值时,随机内存会被修改——不管未初始化的指针偶然指向什么。然后,该函数返回该随机值,使用该值将造成更大的破坏

如果程序员幸运的话,当执行第3行时,他们会得到一个分段错误,因为
a
的随机未初始化值不是有效指针。但从堆栈中提取的值很可能是有效指针。例如,它可能是
b
的值,放置在堆栈上以调用函数。(大多数现代编译器不这样使用调用堆栈,但类似的情况也会发生。)

现在,让我们看看程序中的操作

expression(A) ::= expression(B) PLUS expression(C). {
    *((double *)A)=  *((double *)B)  +  *((double *)C) ;
}
使
A
B
C
无效*并将其强制转换为
双*
会使该操作更难阅读,但可以识别它与上面失败程序中的第3行相同。Lemon操作应该设置左侧非终端的值(在本例中由
A
表示),但该代码假设
A
已经有一个值,产生与上述相同的未定义行为。同样,分段错误可能是一个幸运的结果,因为它可能会突出显示程序,但是在解析器生成器的情况下,与现代编译代码不同,
a
的未初始化值很可能是解析器堆栈上已经存在的某个值

我看不出任何明显的原因,为什么你需要这个计算器中标记的语义值作为指向任何东西的指针。这样做会使代码变得相当复杂;例如,您被迫将每个标记化值存储在向量中(如果输入文本太大,则可能溢出),以便它们都具有唯一的地址。只使用值类型会简单得多:

%token-type { double }
%default-type { double }

expression(A) ::= expression(B) PLUS expression(C).  { A = B + C; }
expression(A) ::= expression(B) MINUS expression(C). { A = B - C;  }
expression(A) ::= expression(B) MULT expression(C).  { A = B * C; }
expression(A) ::= expression(B) DIV expression(C).   {
        if( C != 0)
          A = B / C;
        else
          fprintf(stderr, "%s\n", "Math Error! Divide by zero.");
}

expression(A) ::= number(B). { A = B ;}
然后,您的驱动程序变得简单:

while ((token = lex()) != EOL) {
        parse(parser, token, tkn.val);
}

显然,您希望值具有不同的类型。创建值指针并不能帮助您实现这一目标,因为C中指针的实现,即使是
void*
,也是一个原始内存地址;它不记录任何类型信息。不可能通过查询指针来确定它所指向的数据类型。(因此,将
number
设为指向
double
的指针或指向
int
的指针会丢失有关其原始内容的信息。)如果需要此功能,您的令牌类型需要是
联合
——如果每个令牌和非终端都有一个特定的类型——或者您自己实现的通常称为“有区别的联合”;i、 e.
struct
,它包含一个
联合
和一个说明联合的哪个成员有效的枚举值。但在这两种情况下,值都不是指针(令牌值实际上是指针的情况除外,例如字符串);语义值是令牌对象的直接值,即使该值是一个(希望很小的)
struct

这里有一个简单但错误的程序:

double* add_indirect(double* b, double* c) {
  double *a;
  *a = *b + *c;    /* Undefined behaviour! */
  return a;        /* This, too! */
}
应该清楚该程序错误的原因:
a
从未初始化过。它的声明说它是指向双精度的指针,但它从来没有指向任何东西。因此,当试图通过第3行中的指针存储值时,随机内存会被修改——不管未初始化的指针偶然指向什么。然后,该函数返回该随机值,使用该值将造成更大的破坏

如果程序员幸运的话,当执行第3行时,他们会得到一个分段错误,因为
a
的随机未初始化值不是有效指针。但从堆栈中提取的值很可能是有效指针。例如,它可能是
b
的值,放置在堆栈上以调用函数。(大多数现代编译器不这样使用调用堆栈,但类似的情况也会发生。)

现在,让我们看看程序中的操作

expression(A) ::= expression(B) PLUS expression(C). {
    *((double *)A)=  *((double *)B)  +  *((double *)C) ;
}
使
A
B
C
无效*并将其强制转换为
双*
会使该操作更难阅读,但可以识别它与上面失败程序中的第3行相同。Lemon操作应该设置左侧非终端的值(在本例中由
A
表示),但该代码假设
A
已经有一个值,产生与上述相同的未定义行为。同样,分段错误可能是一个幸运的结果,因为它可能会突出显示程序,但是在解析器生成器的情况下,与现代编译代码不同,
a
的未初始化值很可能是解析器堆栈上已经存在的某个值

我看不出任何明显的原因,为什么你需要这个计算器中标记的语义值作为指向任何东西的指针。那样做