Не работает простой лексер на языке Си
я писал простой лексер для элементарного языка но почему-то в цикле while
функции получения лексемы и записи ее в список не возвращают значений из-за чего программа уходит в вечный цикл.
Текст программы:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* prog.l4
if ?
:= AB & 10:
:= 12 # (:= H) & ABC & 9 ;
:= GG & 5 # F ;
:= 5 & 4 & 3
*/
static FILE *file;
static char buff[128];
typedef enum lexem {
LEX_EOF = -1,
LEX_NPRINT, // ' ' '\n' '\r' '\t'
LEX_SEMICOLON, // ;
LEX_ASSIGN, // :=
LEX_IF, // if
LEX_QMARK, // ?
LEX_COLON, // :
LEX_HASH, // #
LEX_AMPERSAND, // &
LEX_LPAR, // (
LEX_RPAR, // )
LEX_IDENTIFIER, // ABBA
LEX_NUMBER // 228
} lexem_e;
typedef struct lexem_list {
lexem_e lexem;
struct lexem_list *next;
} lexem_list_t;
lexem_e get_next_lexem(FILE *file) {
char ch = fgetc(file);
if (isupper(ch)) {
int i = 0;
do {
buff[i++] = ch;
ch = fgetc(file);
} while(isupper(ch));
fseek(file, -1, SEEK_CUR);
return LEX_IDENTIFIER;
} else if (isdigit(ch)) {
int i = 0;
do {
buff[i++] = ch;
ch = fgetc(file);
} while (isdigit(ch));
fseek(file, -1, SEEK_CUR);
return LEX_NUMBER;
} else if (ch == ':') {
ch = fgetc(file);
if (ch == '=') {
return LEX_ASSIGN;
}
fseek(file, -1, SEEK_CUR);
return LEX_COLON;
} else if (ch == 'i') {
ch = fgetc(file);
if (ch == 'f') {
return LEX_IF;
}
fseek(file, -1, SEEK_CUR);
printf("E Forbidenn symbol %c\n", ch);
exit(-1);
} else if (ch == ';') {
return LEX_SEMICOLON;
} else if (ch == '?') {
return LEX_QMARK;
} else if (ch == '#') {
return LEX_HASH;
} else if (ch == '&') {
return LEX_AMPERSAND;
} else if (ch == '(') {
return LEX_LPAR;
} else if (ch == ')') {
return LEX_RPAR;
} else if (isspace(ch)) {
return LEX_NPRINT;
} else if (ch == EOF) {
return LEX_EOF;
} else {
printf("E Forbidden symbol %c\n", ch);
exit(-1);
}
}
lexem_list_t *add_lexem_to_list(lexem_list_t *head, lexem_e lexem) {
if (!head) {
head = (lexem_list_t *)malloc(sizeof(lexem_list_t *));
head->next = NULL;
head->lexem = lexem;
return head;
}
lexem_list_t *tmp = head;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = (lexem_list_t *)malloc(sizeof(lexem_list_t *));
tmp = tmp->next;
tmp->next = NULL;
tmp->lexem = lexem;
return head;
}
void delete_lexem_list(lexem_list_t *head) {
lexem_list_t *dell;
while (head != NULL) {
dell = head;
head = head->next;
free(dell);
}
}
void print_lexems(lexem_list_t *head) {
while (head->next != NULL) {
printf("L%d ", head->lexem);
head = head->next;
}
}
int main(int argc, char const *argv[]) {
if (argc > 1) {
if (strcmp(argv[1] + (strlen(argv[1]) - 3), ".l4") == 0) {
file = fopen(argv[1], "r");
} else {
printf("E Unsupported file type\n");
return -1;
}
}
if (!file) {
printf("E Can't open file %s\n", argv[1]);
return -1;
}
lexem_list_t *head = NULL;
lexem_e lexem;
while(lexem = get_next_lexem(file) != LEX_EOF) { /* <= Вот тут проблема */
head = add_lexem_to_list(head, lexem);
}
print_lexems(head);
delete_lexem_list(head);
fclose(file);
return 0;
}
Там где стоит метка в коде после первой итерации цикла, переменная lexem
почему_то равно LEX_SEMICOLON
, а переменная head
равна NULL
. Пример программы которую прогоняю в комментарии под заголовком prog.l4
Ответы (1 шт):
Рационально при написании лабораторных работ использовать статический анализатор, например, PVS-Studio. Для студентов есть бесплатные лицензии. С помощью анализатора можно быстро найти многие ошибки и двигаться дальше. Понятно, что высокоуровневую ошибку он может не найти, но простые выявит легко. Это сэкономит время. Например, в этом коде он видит сразу несколько проблем.
Запуск анализатора PVS-Studio на этом коде на сайте Compiler Explorer.
Предупреждения:
head = (lexem_list_t *)malloc(sizeof(lexem_list_t *));
Две такие строчки и два предупреждения:
V641 The size of the allocated memory buffer is not a multiple of the element size.
И действительно, выделяется память под указатель, а не под объект. Памяти недостаточно. При работе с объектом возникнет выход за границу буфера и, как следствие, UB.
} else if (ch == EOF) {
V739 EOF should not be compared with a value of the 'char' type. The 'ch' should be of the 'int' type.
Для хранения результата fgetc
надо использовать int
, а не char
. Иначе буква 'Я' будет восприниматься как EOF
. Подробнее это расписано в описании диагностики.
head = (lexem_list_t *)malloc(sizeof(lexem_list_t *));
V522 There might be dereferencing of a potential null pointer 'head'. Check lines: 99, 98.
Четыре причины проверять, что вернула функция malloc.
И место, где анализатор просто обкричался тремя разными предупреждениями:
while(lexem = get_next_lexem(file) != LEX_EOF) { /* <= Вот тут проблема */
- V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'.
- V768 The expression 'lexem = get_next_lexem(file) != LEX_EOF' is of enum type. It is odd that it is used as an expression of a Boolean-type.
- V559 Suspicious assignment inside the conditional expression of 'while' statement: lexem = get_next_lexem(file) != LEX_EOF.