15.2.3 Вспомогательные функции
К вспомогательным функциям относятся функции Error, SkipBlanks, GetName,
getfloat, match. Необходимо отметить, что почти все указанные функции работают
с глобальным указателем на текущий символ входной строки. Он объявлен и
проинициализированным следующим образом:
char *ch;//объявление глобального указателя
ch=in_line[0];
//инициализация
Функция Error обеспечивает вывод сообщения на экран терминала. Ниже приведен
текст этой функции:
void Error(char *soo) //вывод сообщения об ошибке
{
printf("%s\n",soo);
}
Функция SkipBlanks обеспечивает пропуск служебных символов во входной строке.
Пропуск обеспечивает тем, что статический указатель ch на текущий символ
увеличивается на еденицу. Это процесс продолжается пока текущий символ будет
служебным (табуляция, конец файла, возврат каретки, перевод строки, пробел).
Ниже приведет текст функции
void SkipBlanks()
// пропуск служебных символов
{
while(true) //цикл
пока есть служебное символ
{
switch (*ch)
{
case 8: //табуляция
case 26: //конец файла
case 10: //возврат каретки
case 13: //перевод строки
case
’ ’: //пробел
ch++; break;
default: //если это не служебный символ или конец строки
return;
}
}
}
Функция GetName обеспечивает выделение идентификатора из входной строки.
Первоначально функция вызывает SkipBlanks для пропуска служебных символов.
Затем с помощью функции isalpha, описанной в заголовочном файле ctype.h
проверяется, является ли текущий символ буквой (буковой являются буквы
латиницы). Далее в цикле выбираются буквы или цифры (для проверки используется
библиотечная функция isalnum, описанная в ctype.h)
int
GetName(char *name, int max) //
взять имя во входной строке
{
int i;
SkipBlanks();
//пропустить пробелы
if(isalpha(*ch)) *name++=*ch++; //первый символ должен быть буквой
else
{
*name=0;
return
0;
}
for(i=1; i < max; i++) //взять остальные символы (буква или цифра)
{
if(isalnum(*ch))
*name++=*ch++;
else
{
*name=0;
return 1;
}
}
*name=0;
Error("Большое имя");
return 0;
}
Функция getfloat выделяет из входной строки константу, записывает в строку num и
преобразует ее в вещественное число с помощью библиотечной функции atof,
которая описана в заголовочном файле stdlib.h
double getfloat(char *num, int max) //
взять число во входной строке и преобразовать к double
{
int i = 0, j;
SkipBlanks(); //пропустить пробелы
if(*ch==’-’||
*ch==’+’) //взять знак
num[i++]=*ch++;
SkipBlanks();
//пропустить пробелы
for(; i < max; i++) //взять целую часть числа
{
if(isdigit(*ch))
num[i]=*ch++;
else break;
}
j=i;
if(*ch==’.’) //взять дробную часть числа
{
num[i]=*ch++;
for(j=i+1; j < max; j++)
{
if(isdigit(*ch))
num[j]=*ch++;
else break;
}
}
num[j]=0;
return atof(num); //это стандартная функция Си описана в
<stdlib.h>
}
Функция match производит сопоставление строки str c символами входной строки,
начиная с текущего (по указателю ch). Если строка str полностью содержится в во
входной строке, то указатель ch сдвигается на заданной количество символов. В
противном случае не сдвигается.
int match(char *str) //эта функция разбиралась в разделе "Обработка
строк"
{
char *chv;
SkipBlanks(); //пропустить
служебные символы
chv=ch; //запомнить указатель
while(*str) //цикл проверки совпадения
{
if((*str++)!=(*chv++)) return 0; //вернуть ноль, если не совпала
}
ch=chv; //переместить указатель
return 1; //вернуть значение совпадения
}
15.2.4 Реализация метода рекурсивного спуска
Рекурсивный разбор арифметического выражения начинается с работы
функции ExpressAdd, которая осуществляет разбор и вычисление аддитивных
составляющих арифметического выражения.
TranSin - функция разбора и вычисления синуса;
ExpressFunc - разбор первичного (см. алгоритм РазборD() )
ExpressMult - разбор и вычисление выражений с умножением и делением
ExpressAdd - разбор и вычисление выражений со сложением и вычитанием
AssignOp - вычисление и разбор выражения и присвоение результата переменной
Текст функции разбора и вычисления синуса
void
TranSin(void) // вычисление синуса
{
double f;
if(match("("))
//есть ли скобка ?
{
ExpressAdd(); //вычислить
выражение аргумента синуса
f=Pull(); //взять значение из
стека
Push(sin(f)); //положить в стек
значения синуса
match(")"); //ждать закрывающуюся
скобку
}
else Error("Ошибка в функции sin");
}
void
ExpressFunc(void) // вычисление первичного выражения
{
int num;
char name[30];
double f;
int max=29;
if(match("(")) //вычисление выражения в скобках
{
ExpressAdd(); //разобрать и
вычислить выражение в скобках
if(match(")")==0)
Error("Пропущена )");
}
else
//вычисление функций
{
if(match("sin")) TranSin();
else
//это переменная?
{
if(GetName(name,max)==1)
//взятие значения переменной
{
num=AddNameVar(name); //найти
переменную в таблице
Push(TablVar[num].init); //положить табличное значение в стек
}
else
//взять значение константы
{
f=getfloat(name,max);
Push(f); //положить значение
константы в стек
}
}
}
}
void
ExpressMult(void) //вычисление выражения для умножения и деления
{
double f1, f2;
ExpressFunc(); //вычислить
первичное
while(true)
{
if(match("*")) //если знак умножения
{
ExpressFunc(); //вычислить второе
первичное
f1=Pull(); f2=Pull(); //взять из
стека множители
Push(f1*f2); //положить в стек
произведение
}
else
if(match("/"))//если знак деления
{
ExpressFunc(); //вычислить второе
первичное
f1=Pull(); //взять делитель
f2=Pull(); //взять делимое
if(f1==0.) Error("Деление на
0");
else Push(f2/f1); //положить частное в стек
}
else break;
}
}
void
ExpressAdd(void) // вычисление выражения для сложения и
вычитания
{
double f1, f2;
ExpressMult(); //вычислить
значение первого операнда
while(true)
{
if(match("+"))//если знак плюс
{
ExpressMult(); //вычислить
значение для второго операнда
f1=Pull(); f2=Pull(); //взять
значения слагаемых из стека
Push(f1+f2); //положить сумму в
стек
}
else
if(match("-")) //если это минус
{
ExpressMult(); //взять значение
второго операнда
f1=Pull(); f2=Pull(); //взять
значения из стека
Push(f2-f1); //положить разность в
стек
}
else break;
}
}
void AssignOp(void) //
выполнение операции присваивания
{
char name[10];
int max=9;
int num;
if(GetName(name,max)==1) //взять имя переменной
{
num=AddNameVar(name); //записать
имя в таблицу
}
else
{
ExpressAdd(); //иначе вычислить
выражение
printf("%f\n",Pull());
return;
}
if(match("=")) //имеется операция присваивания
{
ExpressAdd(); //вычислить
выражение
TablVar[num].init=Pull(); //записать значение в таблицу
}
printf("%f\n",TablVar[num].init);
}
void PrintTablVar()
// вывод таблицы символов со значениями
{
int i;
for(i=0; i < TopVar; i++)
{
printf("%2d %10s %f\n",i,TablVar[i].Name,TablVar[i].in
}
}
void main()
{
TopVar=0; //инициализация стека и
таблицы переменных
TopStack=0;
while(1)
{
printf("calc>"); //вывод
подсказки
gets(in_line); //ввод строки
символов
ch=&in_line[0]; //инициализация указателя разбора
if(match("end")) //если это "end", то завершение работы
break;
else
if(match("tabl")) //если это "tabl", то вывести таблицу символов
PrintTablVar();
else
//иначе вычислить значение выражения
AssignOp();
}
}
Пример работы
интерпретатора:
calc>x=100
//вычислить выражение
100.00 //результат
calc>y=200
200.00
calc>z=y/x
2.00
calc>tabl
.//вывести таблицу символов
0 x 100
1 y 200
2 z 2
calc>end
//завершить работу
программы