Значение токена лимонного парсера с типом void *

Я пытался использовать тип void* для моего анализатора лимона, но у меня возникла странная проблема.

Первоначально я использовал пользовательский тип токена, структуру для хранения значений токена, затем я переключился на 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.

И вот мой лексер, который является файлом 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

Я предполагаю, что синтаксический анализатор помещает самое правильное значение в свой стек и использует его везде, где он видит параметр токена.

1 ответ

Решение

Вот простая, но ошибочная программа:

double* add_indirect(double* b, double* c) {
  double *a;
  *a = *b + *c;    /* Undefined behaviour! */
  return a;        /* This, too! */
}

Должно быть понятно, почему эта программа не так: a никогда не был инициализирован. Это объявление говорит, что это указатель на double, но это никогда не делается, чтобы указывать на что-либо. Поэтому, когда делается попытка сохранить значение с помощью этого указателя в строке 3, случайная память модифицируется - независимо от того, на что случайно был указан неинициализированный указатель. Затем это случайное значение возвращается функцией, где его использование создаст больше хаоса.

Если программисту повезет, он получит ошибку сегментации при выполнении строки 3, потому что случайное неинициализированное значение a неверный указатель Но вполне возможно, что значение, полученное из стека, является действительным указателем. Это может быть, например, значение bпомещается в стек для вызова функции. (Большинство современных компиляторов не используют стек вызовов таким образом, но подобные вещи могут происходить.)

Теперь давайте посмотрим на действия в вашей программе.

expression(A) ::= expression(B) PLUS expression(C). {
    *((double *)A)=  *((double *)B)  +  *((double *)C) ;
}

Изготовление A, B а также Cvoid* и приведение их к double* затрудняет чтение этого действия, но оно точно такое же, как в строке 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 теряет информацию о том, что было изначально.) Если вы хотите эту функциональность, ваш тип токена должен быть либо union - если у каждого токена и нетерминала есть определенный тип - или ваша собственная реализация того, что обычно называют "дискриминационным объединением"; то есть struct который содержит как union и значение перечисления, объясняющее, какой член union действует. Но ни в том, ни в другом случае значение не является указателем (исключая случай, когда значение токена действительно является указателем, таким как строка символов); семантическое значение - это прямое значение объекта токена, даже если это значение (надеюсь, небольшое) struct,

Другие вопросы по тегам