C лексический анализатор. Использование переключателя для анализа и подсчета десятичного/не десятичного числа

Мой лексический анализатор распознает цифры (5 555 543 667), десятичные дроби (44,65, 4,1) и точки (.).

Я могу считать цифры, десятичные дроби и точки в порядке, но когда я сталкиваюсь с цифрой и точкой рядом друг с другом, она считается десятичной.

Рассмотрим текстовый файл, который содержит: 555 2,3 55,23 44 5.

Мой вывод будет

1 тип 1: 555
2 тип 3: 2,3
3 тип 3: 55,23
4 тип 1: 44
5 тип 3: 5.

Где тип 3 - мой идентификатор для десятичного числа.

Я бы хотел, чтобы 5-й и 6-й токены считались цифрой, а затем точкой.

Вот как я обрабатываю свой оператор switch.

  switch(*b) {

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    digits:
        t.length++;
        switch(*(b + t.length)) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                goto digits;
            case '.': 
                goto decimal;                   
                break;
            default:
                break;
        }

         t.type = TOKEN_DIGITS;
        t.string = (char *)calloc(t.length + 1, sizeof(char));
        strncpy(t.string, b, t.length);
        break;



    decimal:
        t.length++;
        switch(*(b + t.length)) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                goto decimal;
                break;
            }   
            t.type = TOKEN_DECIMAL;
            t.string = (char *)calloc(t.length+1,sizeof(char));
            strncpy(t.string,b,t.length);           
       break;

Пробовал несколько вещей, но я официально застрял.


person user1819853    schedule 12.10.2013    source источник
comment
Лексический анализ — это не то место, где нужно много переходов. Подумайте о некоторых циклах for и вызовах isdigit().   -  person Charlie Burns    schedule 12.10.2013
comment
Gotos - зло, постарайтесь не привыкать к ним. Что касается вопроса, ваша проблема в том, что вы не знаете дела, пока не обработаете весь элемент. Вам нужно сначала разбить вашу строку на слова, а затем обработать их одно за другим, каждое слово целиком (вместо обработки посимвольно).   -  person SJuan76    schedule 12.10.2013
comment
Не проще ли просто использовать flex? Если это обучающее упражнение, чему вы научились?   -  person rici    schedule 12.10.2013


Ответы (3)


Вам действительно следует использовать функции классификации символов для такого рода упражнений вместо длинных операторов switch. Ваш код станет намного проще, и вам вообще не придется использовать goto.

Например, число может быть описано с помощью следующего регулярного выражения (добавлены пробелы для разделения различных блоков):

[-+]? [0-9]* \.? [0-9]+

Это уже показывает возможные переходы состояний:

  • Номер может (необязательно) начинаться с + или - (если вы поддерживаете числа со знаком)
  • Он может иметь 0..n цифр
  • Если следующий символ не является символом десятичной точки, он должен быть разделителем, в противном случае это недопустимый символ. Если это разделитель, ваш номер прекращается.
  • После запятой должно быть 1..n цифр
  • Номер завершается, когда вы достигаете конца ввода или встречаете разделитель

Все это можно сделать в нескольких строках кода — просто возьмите указатель, указывающий на ваш текущий входной символ, а затем продолжайте шагать вперед один за другим, исследуйте каждый символ и, основываясь на классе символов, решайте, что делать.

Теперь этот конкретный подход не обрабатывает числа с плавающей запятой с использованием экспоненциальной записи и т. Д., Но добавление этих дополнений действительно просто, если вы сделали основы.

person xxbbcc    schedule 12.10.2013

Я думаю, что это дополняет ответ xxbbcc.

*Очень приблизительно * примерно так.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

yylex() {
        int c;
        char *p, buf[1000];

        for(c = get(); isspace(c); c = get());

        if(isdigit(c)) {
                p = buf;
                while(isdigit(c)) {
                        *p++ = c;
                        c = get();

                }
                *p = 0;
                if(c != '.') {
                        unget(c);
                        int i = atoi(buf);
                        return INT;
                }
                assert(c == '.');
                *p++ = c;
                c = get();
                while(isdigit(c)) {
                        *p++ = c;
                        c = get();
                }
                *p = 0;
                float f = atof(buf);
                unget(c);
                return DECIMAL;
        }
}

Много деталей осталось невысказанными. Слежу за EOF. Переполнение буфера. Установка yylval в int или float. Разбор токенов, отличных от простых чисел.

person Charlie Burns    schedule 12.10.2013
comment
Быстрый набор текста - мне потребовалось больше времени, чтобы набрать текстовый ответ. :) Я обычно помещаю такой код в функцию, а затем он вызывается, когда вызывающая сторона сталкивается с чем-то, что начинает число в текущем контексте. - person xxbbcc; 12.10.2013
comment
@xxbbc, твой ответ лучше, может быть, вместе это поможет ОП. Теория и практика. Да, мелкие функции — это хорошо. - person Charlie Burns; 12.10.2013
comment
@ryyker, я примерно сказал. get() и unget() — это общие функции, которые возвращают следующий символ или возвращают его обратно. Может из файла, может из буфера. Их предоставляет Лекс, я сымитировал этот интерфейс. - person Charlie Burns; 12.10.2013

Используйте переменную типа digit_follow_peroid, чтобы сохранить состояние. Каждый раз, когда вы сталкиваетесь с пероидом, устанавливайте для переменной значение false, а затем, когда вы встречаете цифру в блоке десятичного переключателя, устанавливайте для нее значение true. проверьте значение переменной, чтобы определить t.lengthbefore strncpy. Возможно, вам также нужны другие переменные для совместной работы с ним. Лучший способ — определить матрицу перехода состояний, которая намного лучше, чем gotos.

person Timothy Kwok    schedule 12.10.2013