Транслятор
формул (с действительными числами и строками) и новый сканер .
Теперь заменим сканер в нашем трансляторе формул. Напомним, что он обрабатывает плюсы-минусы, умножение и деление. Обрабатывает и скобки. Кроме того имеются и унарные плюс и минус (перед данным числом может быть лишь один унарный знак).
От целых чисел перейдем к действительным (разделитель – точка). Кроме того введем обработку строк. В тексте формулы строка заключается в кавычки – тогда транслятор распознает ее именно как строку символов, а не что-то другое. Таким образом в этой реализации транслятора имеются уже различные типы данных, с которыми работает программа. В строку с обратной польской записью данные-строки помещаются с сохраненной лидирующей (передней) кавычкой. Именно по этой кавычке их в дальнейшем и распознает функция PostFix, обрабатывающая ОПЗ (строку с обратной польской записью).
Сумма строк – это обычная конкатенация (слияние). Дополним алгебру строк такими новыми операциями как вычитание и унарный минус. Вычитание:
“ABC”-“AB”=”C”
“ABC”-“DD”=”ABC”
То-есть если вычитаемое является передним фрагментом, этот фрагмент отбрасывается.
Действие же унарного минуса
приводит к «переворачиванию» строки.
-“ABC”=”CBA”
При наличии ошибок образуются текстовые файлы, содержащие в названии фрагмент err. В частности, ошибка буде при попытке сложить число и строку.
Если результатом формулы является строка, результат выдается с сохраненной передней кавычкой. При необходимости от этой кавычки можно легко избавиться.
Напомню, что строка здесь может быть длиной максимум 100 знаков. Итак, формула в текстовом файле Pr_str.txt (в том же каталоге, где и исполняемый файл транслятора), а результат – в Result.txt.
/****************************/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "Str_ex.h"
#include "QnS.h"
int I=0;
int n=5;
char Lex[150]; /* Это для лексем */
char Res[250]; /* Это для результирующей постфиксной
польской строки */
char End[3]; /* Это конец строки в текстовом файле */
/* Переменные относятся к сканеру */
char Prog[64000]; /* Это для текста программы */
char *pProg; /* Указатель, двигающийся по строке программы */
char LastLex[150]; /* Это для последней лексемы */
char *pProgLast; /* Здесь указатель на начало последней
полученной лексемы */
char *pProgLast2;
/**********************************/
char DelimPol[2]; /* Разделитель в стеке */
char S1[150];
char *pRes;
char Sign1[2]={43,0};
char Sign2[2]={45,0};
char Sign3[2]={42,0};
char Sign4[2]={47,0};
char Sign5[3]={43,85,0};
char Sign6[3]={45,85,0};
void
expr();
/**************************************************************/
/*******************/
/**************************************************************/
/******************************/
/*
sprintf(A,"n=%d",n);*/
/******************************/
int is_Space(int S)
/* Является ли знак пробелом,концом строки,таб-й? */
{
if(S==32|| /* Пробел */
S==10|| /* Перевод строки */
S==13|| /* Возврат каретки */
S==9|| /* Табуляция */
S==11|| /* Верт.табуляция */
S==25) /* Конец носителя */
{
return 1;
}
else
{
return 0;
};
}
int is_Alpha(int S)
/* Является ли знак лат.буквой или _ (подчеркиванием)? */
{
if((S>=65&&S<=90)||
(S>=97&&S<=122)||
S==95)
{
return 1;
}
else
{
return 0;
};
}
int isDelim(int S) /* Является ли знак разделителем, */
{
if(S==43|| /* - */
S==45|| /* + */
S==42|| /* * */
S==47|| /* / */
S==37|| /* % */
S==61|| /* = */
S==60|| /* < */
S==62|| /* > */
S==59|| /* ; */
S==40|| /* ( */
S==41|| /* ) */
S==33|| /* ! */
S==44|| /* , */
S==46) /* . */
{
return 1;
}
else
{
return 0;
};
}
int isDelim1(int S,int SNext) /* Является ли знак разделителем, плюс
учет того, что если после точки цифра, то точка уже не разделитель
(чтобы действительное число с дробной точкой не разбивалось) */
{
if(S==43|| /* - */
S==45|| /* + */
S==42|| /* * */
S==47|| /* / */
S==37|| /* % */
S==61|| /* = */
S==60|| /* < */
S==62|| /* > */
S==59|| /* ; */
S==40|| /* ( */
S==41|| /* ) */
S==33|| /* ! */
S==44|| /* , */
S==46) /* . */
{
if(S=='.')
{
if(isdigit(SNext)!=0)
{
return
0;
}
else
{
return
1;
};
}
else
{
return
1;
};
}
else
{
return 0;
};
}
void ErrorScaner(int N)
{
;
if(N==1)
WrStr("Result_0.txt",End,"Unknown Sign - 1-st in Lexema!
",1);
if(N==2)
WrStr("Result_0.txt",End,"No 2-nd quota",1);
return;
}
void NextLexema() /* Получает очередную лексему */
{
char *pLex=(char *)Lex;
if(*pProg==0) /* Если на входе пустая строка */
{
*pLex=0;
return;
}
/* Сохраняем положение указателя на позицию
в тексте программы (если вдруг придется
возвращаться назад): */
pProgLast2=pProgLast;
pProgLast=pProg;
*pLex=0; /* Для борьбы с "последней лексемой" */
while(is_Space(*pProg)) ++pProg; /* Убираем лидирующие пробелы */
if(*pProg==47&&*(pProg+1)==42) /* Обработка комментария */
{
pProg++;
pProg++;
while(!(*pProg==42&&*(pProg+1)==47))
{
if(*pProg==0)
return;
pProg++;
}
pProg++;
pProg++;
/*return;*/
}
while(is_Space(*pProg))
++pProg; /* Убираем пробелы после комментария */
if(*pProg==45|| /* - */
*pProg==43|| /* + */
*pProg==42|| /* * */
*pProg==47|| /* / */
*pProg==37|| /* % */
*pProg==61|| /* = */
*pProg==60|| /* < */
*pProg==62|| /* > */
*pProg==59|| /* ; */
*pProg==40|| /* ( */
*pProg==41|| /* ) */
*pProg==33|| /* ! */
*pProg==44|| /* , */
*pProg==46) /* . */
{ /* Это мы рассматриваем знаки */
/***/ /* Обработка двухсимвольных знаков */
if(*pProg==60&&*(pProg+1)==61)
{
*pLex=*pProg;
pProg++;
pLex++;
*pLex=*pProg;
pProg++;
pLex++;
*pLex=0;
return;
}
if(*pProg==62&&*(pProg+1)==61)
{
*pLex=*pProg;
pProg++;
pLex++;
*pLex=*pProg;
pProg++;
pLex++;
*pLex=0;
return;
}
if(*pProg==60&&*(pProg+1)==62)
{
*pLex=*pProg;
pProg++;
pLex++;
*pLex=*pProg;
pProg++;
pLex++;
*pLex=0;
return;
}
if(*pProg==33&&*(pProg+1)==61)
{
*pLex=*pProg;
pProg++;
pLex++;
*pLex=*pProg;
pProg++;
pLex++;
*pLex=0;
return;
}
/***/ /* Теперь - односимвольные */
*pLex=*pProg;
pProg++;
pLex++;
*pLex=0;
return;
}
if(*pProg==34) /* Обработка выражения в кавычках */
{
*pLex=34;
pProg++;
pLex++;
while(*pProg!=34)
{
if(*pProg==0)
{
ErrorScaner(2);
return;
}
*pLex++=*pProg++;
}
pProg++;
/* *pLex=34;
pLex++; */
*pLex=0;
return;
}
if(isdigit(*pProg)) /* Начинаем рассматривать число */
{
int
k=0;
while(k>-1)
{
if(*pProg==0)
{
*pLex=0;
return;
}
/*if(*pProg==13)
{
*pLex=0;
return;
}
if(*pProg==10)
{
*pLex=0;
return;
}*/
if(is_Space(*pProg)==1)
{
*pLex=0;
return;
}
if(isDelim1(*pProg,*(pProg+1))==1)
{
*pLex=0;
return;
}
*pLex=*pProg;
pLex++;
pProg++;
}
}
if(is_Alpha(*pProg)) /* Начинаем рассматривать имя переменной или
ключевое слово языка */
{
int
k=0;
while(k>-1)
{
if(*pProg==0)
{
*pLex=0;
return;
}
/*if(*pProg==13)
{
*pLex=0;
return;
}
if(*pProg==10)
{
*pLex=0;
return;
}*/
if(is_Space(*pProg)==1)
{
*pLex=0;
return;
}
if(isDelim(*pProg)==1)
{
*pLex=0;
return;
}
*pLex=*pProg;
pLex++;
pProg++;
}
}
if(!(is_Space(*pProg)!=0||*pProg==0))
ErrorScaner(1);
pProg++;
return;
}
/* Конец "сканерной" части */
void ErrorLex(int N)
{
if(N==1)
{
WrStr("err_lex.txt",End," ",1);
WrStr("err_lex.txt",End,"No Bracket!
",1);
WrStr("err_lex.txt",End,"---",1);
};
if(N==2)
{
WrStr("err_lex.txt",End," ",1);
WrStr("err_lex.txt",End,"ERROR - 0-lexema,
probably phrase is not finished! ",1);
WrStr("err_lex.txt",End,"---",1);
};
if(N==3)
{
WrStr("err_lex.txt",End," ",1);
WrStr("err_lex.txt",End,"ERROR - Wrong
lexema! ",1);
WrStr("err_lex.txt",End,"---",1);
};
if(N==4)
{
WrStr("err_lex.txt",End," ",1);
WrStr("err_lex.txt",End,"ERROR - Last
lexema exists! ",1);
WrStr("err_lex.txt",End,"---",1);
};
if(N==5)
{
WrStr("err_lex.txt",End," ",1);
WrStr("err_lex.txt",End,"ERROR - Long
string! ",1);
WrStr("err_lex.txt",End,"---",1);
}; ;
}
void fact() /* Это множитель - конечный пункт рекурсии */
{
if(*Lex==40)
{
NextLexema();
expr();
if(*Lex!=41)
{
*Res=0;
/*sprintf(Res,"Lexema:
%d ",J-1);
StrCat("ERROR
- No Bracket !!! ",Res);*/
ErrorLex(1);
}
else
{
NextLexema();
}
}
else
{
if(testnum_p(Lex)==0&&(*Lex>47&&*Lex<58)) /* Проверка - является ли действ. числом */
{
StrCat(Lex,Res);
StrCat(DelimPol,Res); /* Добавляем разделитель в стеке */
NextLexema();
}
else
{
if(*Lex==34)
{
if(lenstr(Lex)<100)
{
StrCat(Lex,Res);
StrCat(DelimPol,Res);
}
else
{
ErrorLex(5);
}
NextLexema();
}
else
{
if(*Lex==0)
{
*Res=0;
/*sprintf(Res,"Lexema:
%d ",J-1);
StrCat("ERROR
- 0-lexema, probably phrase is not finished!
",Res);*/
ErrorLex(2);
}
else
{
*Res=0;
/*sprintf(Res,"Lexema:
%d ",J-1);
StrCat("ERROR - Wrong lexema! ",Res);*/
ErrorLex(3);
}
}
};
};
}
void sfact() /* Это множитель со знаком (унарным) */
{
char Lex1[150];
Lex1[0]=0;
if(*Lex==43||*Lex==45)
{
CpStr(Lex,Lex1);
NextLexema();
}
fact();
if(*Lex1!=0)
{
StrCat(Lex1,Res);
StrCat("U",Res);
StrCat(DelimPol,Res);
}
}
void term() /* Обработка умножения-деления */
{
char Lex1[150];
Lex1[0]=0;
sfact();
while(*Lex==42||*Lex==47)
{
CpStr(Lex,Lex1);
NextLexema();
sfact();
StrCat(Lex1,Res);
StrCat(DelimPol,Res);
}
}
void expr() /* Обработка сложения-вычитания */
{
char Lex1[150];
Lex1[0]=0;
term();
while(*Lex==43||*Lex==45)
{
CpStr(Lex,Lex1);
NextLexema();
term();
StrCat(Lex1,Res);
StrCat(DelimPol,Res);
}
}
/********************************/
/* Обработка строки с польской обратной записью */
void ErrorStack(int N)
{
if(N==-2)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"Empty Stack!
",1);
};
if(N==-3)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"Stack Error -
Adding! ",1);
};
if(N==-4)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"Stack Error -
getting (1)! ",1);
};
if(N==-5)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"Stack Error -
getting (2)! ",1);
};
if(N==-6)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"Stack Error -
Adding! ",1);
};
if(N==-7)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"Stack Error - getting in the end! ",1);
};
/* Ощибки с действиями */
if(N==-8)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"+ No number! ",1);
};
if(N==-9)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"- No number! ",1);
};
if(N==-10)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"* No number! ",1);
};
if(N==-11)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"/ No number! ",1);
};
if(N==-12)
{
WrStr("err_st.txt",End," ",1);
WrStr("err_st.txt",End,"+(unar) No
number! ",1);
};
return;
}
int iPF=1;
int PostFix(char *S,char *Dest)
{
void
* pSt;
char
St[100];
int
i,res;
int
iPF1;
int
iPF2;
int
iPF3;
char
*S1;
void
*pS1;
/*char
S2[50];
char
S3[50];
char
S4[100];*/
int r=0;
double F1,F2,F3; /* Для действий с числами */
char sF1[100]; /* Для работы со строками */
char sF2[100];
char
sF3[100];
char sF2_1[100]; /* Для выч-я строк */
int l1;
int isStr1,isStr2; /* Для контроля типов */
if(*S==0)
{
ErrorStack(-2);
return
-2;
}
sF1[0]=0;
sF2[0]=0;
sF3[0]=0;
int iStrW=0; /* Если 0, результат - число (тоже конт.типов)*/
pS1=malloc(100);
if(pS1==NULL)
{
return -1;
}
S1=(char
*)pS1;
*S1=0;
/*WrStr("PostFix.txt",End,"Coming",1);*/
pSt=CreateStack(10000);
while(iPF>0)
{
/*WrStr("PostFix.txt",End,"Cycle",1);*/
iPF1=CpWordN(S,S1,DelimPol[0],iPF);
/* Использ. разд-ль в стеке */
if(iPF1<0)
{
break;
}
iPF++;
if(!(COMPstr(S1,Sign1)==0||COMPstr(S1,Sign2)==0||COMPstr(S1,Sign3)==0||COMPstr(S1,Sign4)==0||
COMPstr(S1,Sign5)==0||COMPstr(S1,Sign6)==0))
/* Если символ не знак, помещаем в стек */
{
for(i==0;i<100;i++) *(St+i)=0;
CpStr(S1,St);
res=AddArrayToStackNoPart(pSt,St,100);
if(res!=100)
{
r=-3;
ErrorStack(-3);
}
}
else /* Если символ - знак */
{
if(COMPstr(S1,Sign1)==0||COMPstr(S1,Sign2)==0||COMPstr(S1,Sign3)==0||COMPstr(S1,Sign4)==0)
{
/****/
*sF1=0;
*sF2=0;
*sF3=0;
/****/
for(i==0;i<100;i++)
*(St+i)=0;
res=GetArrayFromStack(pSt,St,100);
if(res!=100)
{
r=-4;
ErrorStack(-4);
}
if(*St!=34)
{
F1=atof(St);
isStr1=0; /* Для контроля типов */
}
else
{
CpStr(St+1,sF1);
isStr1=1; /* Для контроля типов */
}
for(i==0;i<100;i++)
*(St+i)=0;
res=GetArrayFromStack(pSt,St,100);
if(res!=100)
{
r=-5;
ErrorStack(-5);
}
if(*St!=34)
{
F2=atof(St);
isStr2=0;
}
else
{
CpStr(St+1,sF2);
isStr2=1;
}
if(COMPstr(S1,Sign1)==0)
/* Если сложение */
{
if(isStr1==0&&isStr2==0)
{
F3=F2+F1;
iStrW=0;
}
else
{
if(isStr1==1&&isStr2==1)
{
*sF3=34;
*(sF3+1)=0;
StrCat(sF2,sF3);
StrCat(sF1,sF3);
iStrW=1;
*sF1==0;
*sF2==0;
}
else
{
*sF1=0;
*sF2=0;
*sF3=0;
r=-8;
ErrorStack(-8);
return r;
}
}
}
if(COMPstr(S1,Sign2)==0)
/* Если вычитание */
{
if(isStr1==0&&isStr2==0)
{
F3=F2-F1;
iStrW=0;
}
else
{
if(isStr1==1&&isStr2==1)
{
*sF3=34;
*(sF3+1)=0;
l1=SearchOnFrag(sF2,sF1);
if(l1==0)
{
CpStrNLast(sF2,sF2_1,lenstr(sF1));
StrCat(sF2_1,sF3);
}
else
{
StrCat(sF2,sF3);
}
iStrW=1;
*sF1==0;
*sF2==0;
}
else
{
*sF1=0;
*sF2=0;
*sF3=0;
r=-9;
ErrorStack(-9);
return r;
}
}
}
if(COMPstr(S1,Sign3)==0)
/* Если умножение */
{
if(isStr1==0&&isStr2==0)
{
F3=F2*F1;
iStrW=0;
}
else
{
*sF1=0;
*sF2=0;
*sF3=0;
r=-10;
ErrorStack(-10);
return r;
}
}
if(COMPstr(S1,Sign4)==0)
/* Если деление */
{
if(isStr1==0&&isStr2==0)
{
F3=F2/F1;
iStrW=0;
}
else
{
*sF1=0;
*sF2=0;
*sF3=0;
r=-11;
ErrorStack(-11);
return r;
}
}
if(iStrW==0)
{
sprintf(St,"%f",F3);
}
else
{
CpStr(sF3,St);
iStrW=0;
}
/*Кладем результат в стек*/
/* for(i==0;i<100;i++)
*(St+i)=0; */
res=AddArrayToStackNoPart(pSt,St,100);
if(res!=100)
{
r=-6;
ErrorStack(-6);
}
}
if(COMPstr(S1,Sign5)==0||COMPstr(S1,Sign6)==0)
{
for(i==0;i<100;i++)
*(St+i)=0;
res=GetArrayFromStack(pSt,St,100);
if(res!=100)
{
r=-4;
ErrorStack(-4);
}
/*F1=atof(St);*/
if(*St!=34)
{
F1=atof(St);
isStr1=0;
}
else
{
CpStr(St+1,sF1);
isStr1=1;
}
if(COMPstr(S1,Sign6)==0)
/* Если унарный минус */
{
/*F3=-F1;*/
if(isStr1==0)
{
F3=-F1;
iStrW=0;
}
else
{
InversStr(sF1,sF3);
iStrW=1;
*sF1=0;
}
}
if(COMPstr(S1,Sign5)==0)
/* Если унарный плюс */
{
/*F3=F1;*/
if(isStr1==0)
{
F3=F1;
iStrW=0;
}
else
{
*sF1==0;
*sF2==0;
*sF3==0;
r=-12;
ErrorStack(-12);
return r;
}
}
if(iStrW==0)
{
sprintf(St,"%f",F3);
}
else
{
*St=34;
*(St+1)=0;
StrCat(sF3,St);
iStrW=0;
}
/*Кладем результат в стек*/
/*for(i==0;i<100;i++) *(St+i)=0;*/
res=AddArrayToStackNoPart(pSt,St,100);
if(res!=100)
{
r=-6;
ErrorStack(-6);
}
}
}
}
/*Извлекаем из стека результат*/
for(i==0;i<100;i++) *(St+i)=0;
res=GetArrayFromStack(pSt,St,100);
if(res!=100)
{
r=-7;
ErrorStack(-7);
}
*S1=0;
StrCat(St,S1);
/*WrStr("PostFix.txt",End,"Result
in stack: ",1);
WrStr("PostFix.txt",End,S1,1);*/
/* Конец извлечения результата из стека*/
Del0Last(S1);
/*if(*S1==34)
{
CpStr(S1+1,Dest);
}
else
{
CpStr(S1,Dest);
} Убираем предшеств. кавычку*/
CpStr(S1,Dest);
DeleteStack(pSt);
free(pS1);
return
r;
}
/********************************/
/*******************/
void main(void)
{
Lex[0]=0;
Res[0]=0;
End[0]=13;
End[1]=10;
End[2]=0;
DelimPol[0]=4; /* Задаем разделитель в стеке */
DelimPol[1]=0;
char
dest[200];
/* Чтение файла с программой */
/*****/
FILE *f1;
int Ch;
char ch;
int jc,J;
pProg=Prog;
pProgLast=Prog;
pProgLast2=Prog;
f1=fopen("pr_str.txt","rb");
if(f1==NULL)
{
return;
}
jc=0;
J=0;
/* Чтение программы из файла в строку */
while(jc>-1)
{
Ch=fgetc(f1);
if(Ch==EOF)
{
fclose(f1);
break;
};
ch=(char)Ch;
*(pProg+J)=ch;
J++;
}
/*****/
/* Конец чтения файла с программой */
NextLexema();
expr(); /* Это вход в разбор формулы */
WrStr("Result.txt",End,Res,1); /* Записываем результирующую
польскую постфиксную строку
в файл */
PostFix(Res,dest);
WrStr("Result.txt",End,"А число:",1);
WrStr("Result.txt",End,dest,1);
/*Lex[0]=0;
NextLexema();*/
if(*Lex!=0) /* Это тоже для борьбы с последней лексемой */
{
WrStr("Result.txt",End,"Last lexema exists!",1);
WrStr("Result.txt",End,Lex,1);
ErrorLex(4);
}
return;
}
/***********************/
Это проект Gramm3_002.
Кстати, при желании обработку данных-строк можно легко отключить.
Вообще нужно заметить, что при написании подобных программ нужно быть очень внимательным. Если следовать распространенным образцам обработки скобок, то можно попасть с такую ситуацию, когда формула
(1+2
распознается как ошибочная (нет завершающей скобки), а формула
1+2)
«проскочит».
Может быть и такой эффект, когда формула
(1+2))+4
даст 3 (то-есть учтет лишь 1+2). Все эти варианты нужно обязательно проверять.
Да, чуть не забыл. Здесь все таки имеет смысл опять привести БНФ, ведь добавлена обработка строк (если объект обрамлен кавычками, он распознается как строка).
<expr> := <term> {<add_op><term>}
<term> := <signed_factor>{<mul_op><signed_factor>}
<signed_factor> := [<add_op>]<factor>
<factor> := <float>|”<string>”|(<expr>)
Есть и комментарии (их сканер просто пропускает). Это как в языке Си –
/* Комментарий */. Комментарии должны находиться между лексемами (в данном случае между плюсом и двойкой)
12+/* Коммент */2
и не должны разбивать лексемы, как в этом случае:
1/* Коммент */2+2