Выделение лексем

244
15 декабря 2016, 16:11

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

Ориентировочная схема работы главного цикла программы: 1. Ввод символа из файла 2. Переход автомата в новое состояние в зависимости от введенного символа и текущего состояния 3. Вывод символа в выходной файл, либо другое действие (в зависимости от задания)

Перевод автомата в новое состояние (п. 2) и выбор действия для выполнения (п. 3) могут быть реализованы несколькими способами. От способа реализации зависит максимальный балл за выполнение работы: 1. Выбор действия и состояния с помощью ветвлений (if, switch) – 60% 2. Выбор состояния с помощью таблицы (массива), а действия с помощью ветвления – 80% 3. Выбор состояния и действия с помощью таблиц (для действий используются указатели на функции) – 100%

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

После ограничений пришлось удалить весь код и набросать этот: http://pastebin.ru/NlYjOVPg

Помогите разобраться, как мне полностью выделить лексему, например, несколько 123 - как целое со знаком, а не по отдельности, как 1 - целое со знаком, 2 - целое со знаком, 3 - целое со знаком.

Answer 1

@T2skler, IMHO плодотворной идеей будет сделать для каждого состояния свою таблицу переходов. Причем, ячейка таблицы представляет собой два поля: действие (указатель на функцию) и новое состояние (указатель на соответствующую таблицу).

И для полноты картины введите еще один символ -- EOF.

Тогда достаточно следующих состояний:

  • Start

  • Sign

  • Int

  • Real

и действия:

  • ERR

  • ERR_AND_BRACKET

  • BRACKET

  • INT

  • INT_AND_BRACKET

  • REAL

  • INT_AND_BRACKET

  • NONE

Например, знак в состоянии Start вызывает NONE и переход к Sign, цифра в Sign вызывает NONE и переход в Int, знак в Int вызывает INT и переход в Sign, ... знак в Sign вызывает ERR и переход опять в Sign и т.п.

Теперь о загадочных XXX_AND_BRACKET.

Очевидно, что скобка в Start вызывает BRACKET и переход в Start.

Скобка в Sign с одной стороны должна вызвать ошибку, а с другой -- это ведь просто лексема скобка. Поэтому вызовем ERR_AND_BRACKET, которая сначала вызовет ERR и затем BRACKET, а потом сразу перейдем в Start.

Похожим образом обработаем скобку в состояниях Int и Real. Только вместо ERR вызываем INT или REAL.

EOF в любом состоянии завершает программу (вероятно в состоянии Sign нужно вызвать ERR(?) или добавить действия XXX_AND_EOF, аналогичные XXX_AND_BRACKET)

Наверняка что-то упустил, но Вы уж сами додумайте... Попробуйте, думаю у Вас все получится.

Answer 2

Я года три назад писал небольшой скриптовой язык и интересовался вопросом. Как выше написал @VladD, не стоит изобретать велосипед. Проще всего воспользоваться Flex. Ему задаешь определенные правила, а на выходе он генерирует C'шный код, который уже легко встраиваешь под задачу.

Это сэкономит тебе кучу времени, а в итоге получешь отличный код.

P.S. Если не ошибаюсь он умеет и в C++.

Answer 3

Если кол-во состояний конечно и можно запоминать только номер текущего состояния и последний введённый символ, то узнать что мы прочитали 123, а не 213 нельзя. В противном случае кол-во состояний было бы пропорционально вводу или сам ввод (или позиции в нём) необходимо отдельно хранить.

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

Например, ragel позволяет создать конечный автомат из регулярного выражения:

%%{
    machine number_fsm;
    integer = ('+' | '-')? digit+;
    float = ('+' | '-')? (digit+ ('.' digit*)? | '.' digit+)
            (('e' | 'E') ('+' | '-')? digit+)?;
    paren = '(' | ')';
    token = integer | float | paren;
    main := token**;
}%%

. Чтобы получить эту картинку, необходимо сохранить вышеприведённый код в number_fsm.rl файл и выполнить команды:

$ ragel -F1 -p -V -o number_fsm.dot number_fsm.rl
$ dot -Tpng -o number_fsm.png number_fsm.dot
$ display number_fsm.png

ragel также умеет генерировать таблицы перехода для состояний и исполнять действия в любом месте на выбранном языке (например, С, Go).

Answer 4

Не стройте велосипед, воспользуйтесь lex/flex.

READ ALSO
Убрать системное меню навигации

Убрать системное меню навигации

Есть приложение на QT C++ для AndroidСтоит задача развернуть приложение на весь экран

212
Генератор псевдослучайных чисел

Генератор псевдослучайных чисел

Пишу на С++Задача в состоит в том, чтобы посчитать значения функции,используя генератор случайных чисел

223
Winforms vs winapi

Winforms vs winapi

У меня старая ОС(windows xp) и компилятор(mvs 2010) на данный момент доступны только две технологии писать свои форточки winforms и голый winapi, какую технологию...

225
Запросить по ip, указав при этом HOST Qt 5+

Запросить по ip, указав при этом HOST Qt 5+

Стоит задача получить страницу с сайта, войдя по ip, при этом указать HOST и путь страницыРеализация нужна для Qt 5+ (так уж настроена сеть)

203