Главная arrow Программирование С++ arrow Код синтаксического анализатора выражений

Код синтаксического анализатора выражений

Полный код анализатора выражений для интерпретатора Mini С++ приведен в листинге 9.1. Его нужно поместить в файл parser.cpp. Функционирование синтаксического анализатора описано в последующих разделах.
Листинг 9.1. Рекурсивно-нисходящий анализатор для целочисленных выражений
#include <iostream> #include <cstring> #include <cstdlib> #include <cctype> #include "mccommon.h"
using namespace std;
II Таблица поиска ключевых слов.
// Ключевые слова должны быть набраны строчными буквами, struct commands {
char command[20];
token_ireps tok; } com_table[] = (
"if", IF,
"else", ELSE,
"for", FOR.
"do", DO,
"while", WHILE,
"char", CHAR,
"int",  INT,
"return", RETURN,
"switch", SWITCH,
"break", BREAK,
"case", CASE,
"cout", COUT,
"cin", CIN,
"", END   // помечает конец-таблицы
};
// Эта структура связывает имя библиотечной функции // с указателем на эту функцию, struct intern_func_type {
char *f_name; // имя функции
int (*р)();     // указатель на функцию } intern_func[] = {
"getchar", call^getchar,
"putchar", call_putchar, . "abs", call_abs,
"rand", call_rand,
"", 0   // нуль-завершение перечня
};
// Точка входа в анализатор, void eval_exp(int fcvalue) {
get_token();
if(!*token) {
throw InterpExc(NO_EXP);
}
if(*token == ';')  {
value =0; // пустое выражение return;
}
eval_exp0(value);
putbackO; // возвращает последнюю прочитанную лексему // во входной поток
}
// Обрабатывает выражение присваивания.
void eval_expO(inc &value)
{
// temp содержит имя var, получающей присваивание, char temp[MAX_ID_LEN+1];
tok_types temp_tok;
if(token_type == IDENTIFIER) {
if(is_var(token))    {    // если переменная, то не присваивание ли? strcpy(temp, token); temp_tok = token_type; get_token();
if(*token ==•=•)    {   // присваивание get_token();
eval_exp0(value); // получает значение для присваивания assign_var(temp, value); // присваивает значение return;
}
else   {    //не присваивание putbackO; // возвращает исходную лексему во входной поток strcpy(token, temp);
token_type = temp_tok;
>
}
eval_expl(value);
}
// Обрабатывает операции отношения, void eval_expl(int &value)
int partial_value; char op;
char relopsf] = {
LT,  LE, GT, GE, TEQ, NE,  0
>;
eval_exp2 (value) ; op = *token;
if(strchr(reldps, op)) { get_token();
eval_exp2(partial_value);
switch(op)    {    // выполняет операцию отношения case LT:
value = value < partial_value;
break; case LE:
value = value <= partial_value;
break; case GT:
value = value > parcial_value;
break; case GE:
value = value >= partial_value; break; case EQ:
value = value == partial_value; break;
}
case NE:
value = value != partial_value; break;
J
J
}
// Складывает или вычитает два терма.
void eval_exp2(int &value)
{
char   op;
int partial_value; char okops[] = {
'(',  INC, DEC, •+',  0
};
eval_exp3(value);
while((op = *token) == '+'  || op == '-') { get_token();
if(token_type == DELIMITER && !strchr(okops, * token)) throw InterpExc(SYNTAX);
eval_exp3(partial_value);
switch(op)    {   // добавляет или вычитает case
value = value - partial_value; break; case '+':
value = value + partial_value; break;
}
}
}
// Умножает или делит два фактора.
void eval_exp3(int fcvalue) {
char   op;
int partial_value, t; char okopsf] = {
' (',  INC, DEC,   '-' ,   4-',  0
};
eval_exp4(value);
while((op = * token) == '*'  || op == '/' || op == '%')  { get_token();
if(token_type == DELIMITER && !strchr(okops, * token)) throw InterpExc(SYNTAX);
eval_exp4(partial_value);
switch(op)    {    // умножение, деление нацело или остаток от деления // нацело
case '*':
value = value * partial_value;
break; case '/':
if(partial_value == 0)
throw InterpExc(DIV_BY_ZERO);
value = (value) / partial_value;
break; case 1 %':
t = (value) / partial_value;
value = value - (t * partial_value);
break;
}
}
}
// Унарные +, -, ++, или —.
void eval_exp4(int fcvalue) {
char   op; ,
char temp;
op = '\0';
if(*token == •+•  || *token == '-'  || ¦token == INC || *token == DEC)
{
temp = * token; op = *token; get_token(); if(temp == INC)
assign_var(token, find_var(token) +1); if(temp == DEC)
ass ign_var(token, find_var(token) -1);
}
eval_exp5(value);
if(op == '-') value = -(value);
}
// Обрабатывает выражение со скобками.
void eval_exp5(int fcvalue)
{
if((*token == ' (')) { get_token();
eval_expO(value); // получает подвыражение
if(*token != ')')
throw InterpExc(PAREN_EXPECTED); get_token();
}
else
acorn(value);
}
// Находит значение числа, переменной или функции.
void atom (int fcvalue) {
int i;
char temp[MAX_ID_LEN+1];
i
swi tch(token_type)  { case IDENTIFIER:
i = internal_func(token); if(i != -1)  {
// Вызывает функцию стандартной библиотеки ("standard library").
value = (*intern_func[i].p)(); } *
else if(find_func(token)) {
// Вызывает функцию, созданную программистом.
call();
value = ret_value;
}
else {
value = find_var(token); // получает значение переменной strcpy(temp, token); // сохраняет имя переменной
// Проверяет операции ++ или —. get_token();
if(*token == INC || *token == DEC)  { if(*token == INC)
assign_var(temp, find_var(temp)+1); else
assign_var(temp, find_var(temp)-1); } else putback();
}
get_token(); return;
case NUMBER: // числовая константа value = atoi(token); get_token();
return;
case DELIMITER: // проверяет, не символьная ли константа
if(*token == ' \" ') { value = *prog; prog++;
if(*prog! = "\'1)
throw InterpExc(QUOTE_EXPECTED); prog++; get_token();
return ;
}
if(*token==')') return; // обрабатывает пустое выражение else throw InterpExc(SYNTAX); // иначе синтаксическая ошибка default:
throw InterpExc(SYNTAX); // синтаксическая ошибка
}
}
// Отображает сообщение об ошибке, void sntx_err(error_msg error) {
char *p, *temp; int linecount = 0;
static char *e[]= { "Syntax error", "No expression present", "Not a variable", "Duplicate variable name", "Duplicate function name", "Semicolon expected", "Unbalanced braces", "Function undefined", "Type specifier expected", "Return without call", "Parentheses expected", "While expected", "Closing quote expected", "Division by zero",
"{ expected (control statements must use blocks)".
"Colon expected"
// Выводит ошибку и номер строки, cout « "\n" « е[error]; р - p_buf;
while(р != prog)    {   // находит номер строки с ошибкой P++;
if(*р == '\г') { linecount++;
}
}
cout « " in line " « linecount « endl; temp = p;
while(p > p_buf && *p != '\n') p—;
// Отображает ошибочную строку, while(p <= temp) cout « *p++;
cout « endl;
>
// Получает лексему. tok_types get_token() {
char *temp;
token_type = UNDEFTT; tok = UNDEFTOK;
temp = token; *temp = '\0';
// Пропускает пробелы.
while(isspace(*prog) && *prog) ++prog;
// Пропускает переход на новую строку, while(*prog == '\r')  {
};
++prog; ++prog;
// Снова пропускает пробелы.
while(isspace(*prog) && *prog) ++prog;
// Проверяет, не конец ли программы. if(*prog == 'NO') {
¦token = ' \0';
tok = END;
return (token_type = DELIMITER) ;
// Проверяет ограничители блока, if(strchr("", *prog)) {
¦temp = ¦prog;
temp++;
¦temp = '\0';
prog++;
return (token_type = BLOCK) ;
}
// Ищет комментарии, if(*prog == •/')
if(Mprog+l) =»'*')    {   // знак комментария /* prog += 2;
do   {   // находит конец комментария
while(*prog != •*') prog++; . prog++;
} while (¦prog != "/'); prog++;
П return (token_type = DELIMITER);
} else if(*(prog+l) == '/') { // знак комментария // prog += 2;
// Находит конец комментария.
while(^prog != '\r' && ^prog != '\0') prog++;
if(*prog == '\r1) prog +=2;
return (token_type = DELIMITER);
II Проверяет двойные знаки операций, if(strchr("!<>=+-", *prog))  { switch(*prog) { case 1=1:
.  if(*(prog+1) == '=') { prog++; prog++; ¦temp = EQ;
temp++; *temp = EQ; temp++; ¦temp = '\0';
}
break; case •!•:
if (Mprog+1) == ' = ') { prog++; prog++; ¦temp = NE;
temp++; ¦temp = NE; temp++; ¦temp = '\0';
}
break; case •<':
if(*(prog+1) == •=') { prog++; prog++;
¦temp = LE; temp++; ¦temp = LE;
}
else if(¦(prog+1) == •<•) { prog++; prog++;
¦temp = LS; temp++; ¦temp = LSr
}
else { prog++; ¦temp = LT;
}
temp++; ¦temp = '\0'; break; case '>':
if(¦(prog+1) == '=')  { prog++; prog++;
¦temp = GE; temp++; *temp = GE; } else if(*(prog+1) == ">') { prog++; prog++;
*temp = RS; temp++; *temp = RS;
}
else { prog++; *temp = GT;
}
temp++; *terop = '\0'; break; case '+':
if(*(prog+l) == '+') { prog++; prog++;
*temp = INC; terap++; *temp = INC;
terap++;
*temp = '\0';
}
break; case * - ':
if(*(prog+1) == '-')  { prog++; prog++;
¦temp = DEC; temp++; ¦temp = DEC;
temp++;
¦temp = '\0*;
}
break;
}
if(¦token) return(token_type = DELIMITER);
}
// Проверяет другие ограничители, if(strchr("+-*Л/%=;:(),¦", ¦prog))  {
¦temp = ¦prog;
prog++;
temp++;
¦temp = '\0';
return (token_type = DELIMITER) ;
// Читает строку в кавычках, if(*prog == "") { prog++;
while(*prog != '"' && *prog != '\r' && *prog) { // Ищет escape-последовательность \n. if(*prog == 'W') { if(*(prog+l) == 'n') { prog++;
*temp++ = ' \n';
}
}
else if((temp - token) < MAX_T_LEN) *temp++ = *prog;
prog++;
}
if(*prog == '\r"  || *prog == 0)
throw InterpExc(SYNTAX); prog++; *temp = ' \0'; return (token_type = STRING);
}
// Читает целое число, if(isdigit(*prog))  {
while (! isdel im (*prog))  {
if ((temp - token) < MAX_ID_LEN)
*temp++ = *prog; prog++;
}
*temp = *\0';
return (token_type = NUMBER);
}
// Читает идентификатор или ключевое слово, i f(isalpha(*prog))  {
while(!isdelim(*prog))  {
}
if((temp - token) < MAX_ID_LEN)
*temp++ = *prog; prog++;
}
token_type = TEMP;
}
*temp = '\0';
// Определяет, является ли лексема ключевым словом
// или идентификатором.
if(token_type == TEMP) {
tok = look_up(token); // преобразует во внутреннюю форму if(tok) token_type = KEYWORD; // ключевое слово else token_type = IDENTIFIER;
}
// Проверяет неидентифицированный символ в файле, if(token_type == UNDEFTT) throw InterpExc(SYNTAX);
return token_type;
}
// Возвращает лексему во входной поток.
void putback()
{
char *t; t = token;
for(; *t; t++) prog—;
}
// Ищет внутреннее представление лексемы //в таблице лексем. token_ireps look_up(char *s) {
int i; char *p;
// Преобразует в нижний регистр, р = s;
while(*p) { *р = tolower(*p); р++;    }
// Проверяет, есть ли лексема в таблице. for(i=0; *cam_table[i].command; i++) { if(!strcmp(com_table[i].command, s)) return com_table[i].tok;
}
return UNDEFTOK; // неизвестная команда
}
// Возвращает индекс внутренней библиотечной функции // или -1, если она не найдена, int internal_func(char *s) {
int i;
for(i=0; intern_func[i].f_name[0]; i++)  {
if(!strcmp(intern_func[i].f_name, s))   return i;
}
return -1;
}
// Возвращает true, если с — ограничитель.
bool isdelim(char с)
{
if(strchr(" !:;,+-<>'/*%Л=()с)  || с == 9 ||
с == '\r'  II с == 0) return true; return false;
}
Анализатор использует следующие глобальные переменные и перечислимые типы (они объявлены в заголовочном файле mccommon.h, который будет приведен далее в этой главе).
const int MAX_T_LEN   = 128;     // максимальная длина лексемы
const int MAX_ID_LEN = 31;       // максимальная длина идентификатора
// Перечислимый тип для типов лексем.
enum tok_types { UNDEFTT, DELIMITER,  IDENTIFIER,
NUMBER, KEYWORD.  TEMP.  STRING, BLOCK };
// Перечислимый тип внутреннего представления лексем, enum token_ireps { UNDEFTOK, ARG, CHAR,  INT, SWITCH,
CASE,  IF,  ELSE,  FOR, DO, WHILE, BREAK,
RETURN, COUT, CIN,  END };
// Перечислимый тип для двухсимвольных операций, таких как <=. enum double_Ops { LT=1, LE, GT, GE, EQ, NE, LS, RS,  INC, DEC };
// Константы, используемые для генерации
// исключения при наличии синтаксических ошибок.
//
// Замечание: SYNTAX — общее сообщение об ошибке, используемое, когда // нет более подходящего, enum error_msg
{ SYNTAX, NO_EXP, NOT_VAR, DUP_VAR, DUP_FUNC,
SEMI_EXPECTED, UNBAL_BRACES,  FUNC_UNDEF,
TYPE_EXPECTED, RET_NOCALL,  PAREN_EXPECTED,
WHILE_EXPECTED, QUOTE_EXPECTED, DIV_BY_ZERO,
BRACE_EXPECTED, COLON_EXPECTED };
extern char *prog;    // текущая позиция в исходном коде extern char *p_buf; // указывает на начало буфера программы
extern char token[MAX_T_LEN+1]; // строковая версия лексемы extern tok_types token_type; // содержит тип лексемы extern token_ireps tok; // внутреннее представление лексемы
extern int ret_value; // значение, возвращаемое функцией
// Класс-исключение для Mini С++, class InterpExc {
error_msg err; public:
InterpExc(error_msg e) { err = e; } error_msg get_err()  { return err; }
};
В переменной prog указывается текущая позиция в исходном коде, на которой находится выполняющаяся программа в данный момент. Таким образом, в ней содержится адрес, начиная с которого будет читаться интерпретатором следующий фрагмент программы. Указатель p_buf не меняется интерпретатором и всегда указывает на начало интерпретируемой программы Текущая лексема (token) хранится в переменной token (лексема — это неделимая часть кода программы). Тип лексемы содержится в переменной token_type. Переменная tok хранит внутренний формат лексемы, если она представляет собой ключевое слово.
В перечислимом типе (enumeration) tok_types объявлены типы лексем, распознаваемые интерпретатором Mini С++. Перечислимый тип token_ireps описывает внутренний формат лексем, представляющих собой ключевые слова. Константы, обозначающие двухсимвольные знаки операций, такие как <=, заданы в перечислимом типе doubie_ops. Различные коды ошибок перечислены в типе error_msg. Наконец, класс-исключение InterpExc предназначен для сообщения об ошибках.
 
раскраски для детей играть онлайн бесплатно .