Как построить парсер рекурсивного спуска

Я работал над парсером рекурсивного спуска для простого калькулятора. Когда что-то объявляется, оно объявляется как int или как float. В настоящее время я сохраняю строки в два разных вектора, один для int и один для float. На данный момент мне все равно, какие числа связаны, я просто забочусь о том, что строка объявляется перед ее использованием.

Моя проблема заключается в том, что я должен иметь возможность выводить предупреждающее сообщение, если int и float используются в такой операции, как float + int.

Так что, если выражение это термин + выражение или термин-выражение или термин. В рекурсивном спуске, как я могу проверить, используется ли int в операции с плавающей точкой? Извините, если объяснение не ясно. Мне сложно это объяснить. Я добавил код, если это необходимо, я просто не хотел наводнить вопрос кодом.

редактировать:
все еще отсутствует куча кода, я решил просто взять важную часть, но я могу загрузить все это, если потребуется. Я вижу, что некоторые люди не поняли, что было главным вопросом. Одно из требований: «Когда целочисленные значения и значения с плавающей точкой смешаны в +, -, * и /, целое число преобразуется в число с плавающей точкой. Распечатайте сообщение с указанием номера строки и того, что преобразование будет необходимо». На данный момент программа читает из файла. если вы говорите «Int X;» в настоящий момент программа будет сохранять x в векторе int, а затем, когда вы говорите что-то вроде x = 5; он подтвердит, что x был объявлен, и назначение пройдет. моя проблема в том, где, если вы говорите, INT X; плавать у; int z; х = 5; у = 7,5; г = х + у; как бы я мог проверить это, так как в данный момент моя программа сохраняет только тип переменных, а не их значение. По сути, мне интересно, можно ли было бы сделать что-то вроде сканирования завершенного разбора, как если бы это была строка, или какой-то другой метод обнаружения операции с использованием int и float выполняется.

сканер lex создан с помощью flex

class Token {
Tokentype   type;
string      value;
int     linenum;

public:
Token(Tokentype t, string v="") {
type = t;
value = v;
}
Tokentype getType() { return type; }
string getValue() { return value; }
int getLinenum() { return linenum; }
};

vector<string> int_list;
vector<string> float_list;

class PTree {
PTreeNodetype   type;
PTree *left;
PTree *right;
public:
PTree(PTreeNodetype t, PTree *l=0, PTree *r=0) {
type = t;
left = l;
right = r;
}
PTreeNodetype getType(){ return type;}
};

// expr ::= term PLUS expr | term MINUS expr | term
PTree *
Expr() {

PTree *term = Term();
Token *t;

if (!term)
return 0;

t = getToken();

if (t == NULL){
delete t;
return 0;
}
if(t->getType() != T_SC)
{
if (t->getType() == T_RPAREN){
pushbacktoken(t);
return new PTree(EXPR, term);
}

if (t->getType() != T_PLUS && t->getType() != T_MINUS)
{
cout << t->getLinenum() <<  ":" << "Error:    expected + or -" << endl;
pushbacktoken(t);
delete t;
return 0;
}delete t;
PTree *expr = Expr();

if (!expr)
return 0;

return new PTree(EXPR, term, expr);
}

pushbacktoken(t);
return new PTree(EXPR, term);
}

1

Решение

Я думаю, что вам нужно объяснить структуру вашего кода немного подробнее.

В таком переводчике, о котором вы говорите, обычно происходят три вещи:

  1. Лексер / сканер генерирует поток токенов
  2. Парсер берет токен и строит семантические объекты
  3. Интерпретатор использует дерево семантических объектов и выполняет их

Этап 1 не должен заботиться о том, что вы добавляете int и float. Этап 2 может заполнить поле предупреждения в вашем семантическом объекте / структуре, которое интерпретатор напечатает, когда увидит заполненный объект, или интерпретатор может распознать это условие предупреждения самостоятельно.

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

2

Другие решения

Два варианта, которые я вижу, зависит от того, что вы делаете.

Первый. Не беспокойтесь об этом, пока вы создаете дерево разбора. Позже, когда вы идете по дереву, вы можете легко проверить это и выдать ошибку.

Во-вторых. Используйте разные правила для int а также float, Таким образом, у вас будет правило для добавления двух целых чисел и правило для добавления двух чисел с плавающей запятой. Это также означает, что у вас не будет number Правило, которое я предполагаю, что вы делаете, которое смешивает как целые и плавающие.

Я определенно рекомендую первый способ.

1

Калькуляторы традиционно не «объявляют» вещи, поэтому неясно, что знает ваш калькулятор, когда он анализирует выражение.

Если я предполагаю, что вы «объявляете i int, r real» до разбора выражения «i * r», у вас возникает несколько вопросов:

а) как вы узнаете при разборе, были ли i и r объявлены? Технический ответ заключается в том, что во время анализа вам не нужно знать; Вы можете разобрать, построить дерево и выполнить такую ​​проверку позже. На практическом уровне люди часто вплетают поиск символов в процесс синтаксического анализа (это усложняется по мере того, как ваш язык становится больше, поэтому это не рекомендуется для других, кроме калькуляторов [вы обнаружите, что большинство компиляторов C делают это, добавляя их беспорядку) ]). Ответ прост: держите вокруг себя список определенных символьных строк, а когда вы встретите идентификатор, посмотрите, есть ли он в списке.

б) откуда вы знаете тип «я» или «г»? Легко. Связать с символьной строкой объявленного типа, например,,. Связанные наборы объявлений обычно называют таблицами символов.

в) как узнать, работают ли операции с одинаковыми («правильными») значениями? Здесь нужно связать с каждым операндом свой «тип». Константы имеют очевидный тип; 1.0 является действительным, 1 является целым числом. «i» является целым числом, и ваш синтаксический анализатор знает это, потому что он искал тип (выше); аналогично для «р». Затем каждый член выражения должен проверять свои операнды на совместимость. Что может быть неочевидным, так это то, что каждое выражение должно вычислять его тип результата, например, 3 * 4,0 является действительным, а не целым числом. Таким образом, параллельно с механизмом синтаксического анализа вам нужно распространять тип.

1

+1 к voidlogic. Его ответ должен дать вам базовое представление о том, как создать парсер рекурсивного спуска. Если у вас возникли проблемы с определенной частью вашей, было бы неплохо получить немного больше информации о том, как вы структурируете свой код.

Если вы хотите увидеть пример одного, посмотрите на это реализация.

0

Вот книга, которая может помочь:

  • Составители: Принципы, Техника и Инструменты («Книга Дракона») А. Ахо, М. Лама и Р. Сетхи.

Вот набор инструментов, которые могут вам помочь:

  • GNU flex
  • GNU бизон
0
По вопросам рекламы [email protected]