it-roy-ru.com

Программа Lexical Analyzer C для идентификации токенов

Я написал C-программу для анализатора Lex (небольшой код), который будет определять ключевые слова, идентификаторы и константы. Я беру строку (исходный код C в виде строки), а затем преобразовываю ее в слова. 

#include <stdio.h>
#include <conio.h>
#include <string.h>

char symTable[5][7] = { "int", "void", "float", "char", "string" };

int main() {
    int i, j, k = 0, flag = 0;
    char string[7];
    char str[] = "int main(){printf(\"Hello\");return 0;}";
    char *ptr;
    printf("Splitting string \"%s\" into tokens:\n", str);
    ptr = strtok(str, " (){};""");
    printf("\n\n");
    while (ptr != NULL) {
        printf ("%s\n", ptr);

        for (i = k; i < 5; i++) {
            memset(&string[0], 0, sizeof(string));
            for (j = 0; j < 7; j++) {
                string[j] = symTable[i][j];
            }

            if (strcmp(ptr, string) == 0) {
                printf("Keyword\n\n");
                break;
            } else
            if (string[j] == 0 || string[j] == 1 || string[j] == 2 ||
                string[j] == 3 || string[j] == 4 || string[j] == 5 ||
                string[j] == 6 || string[j] == 7 || string[j] == 8 ||
                string[j] == 9) {
                printf("Constant\n\n");
                break;
            } else {
                printf("Identifier\n\n");
                break;
            }
        }
        ptr = strtok(NULL, " (){};""");
        k++;
    }
    _getch();
    return 0;
}

С помощью приведенного выше кода я могу идентифицировать ключевые слова и идентификаторы, но не могу получить результат для чисел. Я пытался использовать strspn(), но безрезультатно. Я даже заменил 0,1,2...,9 на '0','1',....,'9'

Любая помощь будет оценена.

2
Manoj Kandala

Вот некоторые проблемы в вашем парсере:

  • Тест string[j] == 0 не проверяет, является ли string[j] цифрой 0. Символы для цифр пишутся от '0' до '9', их значения от 48 до 57 в ASCII и UTF-8. Кроме того, вы должны сравнивать *p вместо string[j], чтобы проверить, есть ли в строке цифра, обозначающая начало числа.

  • Разделять строку с помощью strtok() не очень хорошая идея: она изменяет строку и перезаписывает первый разделитель символ с '\0': это предотвратит сопоставление таких операторов, как (, )...

  • Строка " (){};""" точно такая же, как " (){};". Чтобы экранировать " внутри строк, вы должны использовать \".

Чтобы написать лексер для C, вы должны включить первый символ и проверить следующие символы в зависимости от значения первого символа:

  • если у вас есть пробел, пропустите
  • если у вас есть //, это комментарий к строке: пропустите все символы до новой строки.
  • если у вас есть /*, это блочный комментарий: пропустите все символы, пока не получите пару */.
  • если у вас есть ', у вас есть символьная константа: анализируйте символы, обрабатывая escape-последовательности, пока не получите закрывающий '.
  • если у вас есть ", у вас есть строковый литерал. сделать так же, как для символьных констант.
  • если у вас есть цифра, используйте все последующие цифры, у вас есть целое число. Для синтаксического разбора синтаксиса полного числа требуется гораздо больше кода: оставьте это на потом.
  • если у вас есть буква или подчеркивание: используйте все последующие буквы, цифры и подчеркивания, а затем сравните Word с набором предопределенных ключевых слов. У вас есть либо ключевое слово, либо идентификатор.
  • в противном случае у вас есть оператор: проверьте, являются ли следующие символы частью оператора из 2 или 3 символов, например == и >>=.

Вот и все об этом для простого C-парсера. Полный синтаксис требует больше работы, но вы попадете туда по одному шагу за раз.

3
chqrlie

Когда вы пишете лексер, всегда создавайте определенную функцию, которая находит ваши токены (имя yylex используется для инструмента System Lex , поэтому я использовал это имя). Написание лексера в main не является разумной идеей, особенно если вы хотите использовать синтаксис, семантический анализ позже. 

Из вашего вопроса не ясно, хотите ли вы просто выяснить, что такое числовые токены, или вы хотите токен + получить числовое значение. Я приму первый. 

Это пример кода , который находит целые числа:

int yylex(){

    /* We read one char from standard input */
    char c = getchar();

    /* If we read new line, we will return end of input token */
    if(c == '\n')
        return EOI;

    /* If we see digit on input, we can not return number token at the moment. 
         For example input could be 123a and that is lexical error  */
    if(isdigit(c)){

        while(isdigit(c = getchar()))
            ;

        ungetc(c,stdin);
        return NUM;
    }

    /* Additional code for keywords, identifiers, errors, etc. */
}

Токены EOI, NUM и т.д. Должны быть определены сверху. Позже, когда вы захотите написать синтаксический анализ, вы будете использовать эти токены, чтобы выяснить, отвечает ли код синтаксису языка или нет. В лексическом анализе обычно значения ASCII вообще не определены, например, ваша функция лексера просто возвращает ')'. Зная это, токены должны быть определены выше 255 значения. Например:

#define EOI 256
#define NUM 257

Если у вас есть еще вопросы, не стесняйтесь спрашивать. 

0
Aleksandar Makragić

string[j]==1

Этот тест неверен(1) (во всех реализациях C, о которых я слышал), поскольку string[j] - это некий char, например, использование ASCII (или UTF-8 , или даже старого EBCDIC , используемого на мэйнфреймах IBM) и кодирование char цифры 1 - это не число 1. На моей машине Linux/x86-64 (и на большинстве машин, использующих ASCII или UTF-8, например почти все), использующих UTF-8, символ1 кодируется как байт код 48 (то есть (char)48 == '1')

Вы, вероятно, хотите

string[j]=='1'

и вам следует рассмотреть возможность использования стандартной функции isdigit (и связанной с ней).

Помните, что UTF-8 практически используется везде но является кодировкой многобайтовой (отображаемых символов). Смотрите это ответ .


Примечание (1): тест string[j]==1, вероятно, тоже неуместен! Возможно, вы могли бы проверить isdigit(*ptr) в каком-то лучшем месте.

PS. Пожалуйста, возьмите в привычку компилировать все предупреждения и отладочную информацию (например, с помощью gcc -Wall -Wextra -g, если используете GCC ...) и использовать отладчик (например, gdb). Вы должны были узнать свою ошибку за меньшее время, чем вы получили, чтобы получить ответ здесь.

0
Basile Starynkevitch