Метки
и оператор GOTO.
Как-то в Интернете я наткнулся на диаграмму Вирта для языка, похожего на Паскаль. Этот документ натолкнул меня на мысль, как можно «обработать» метки. Введем не просто оператор, а «Общий оператор». Общий оператор может быть либо помеченным оператором, либо просто оператором. Помеченный же оператор состоит из метки на первом месте, потом двоеточия и, наконец, оператора. Вот БНФ:
<Программа>:=<Поток операторов>
<Поток операторов>:=[<Общий оператор><;>]
<Общий оператор>:=<Помеч.оператор>|<Оператор>
<Помеч.оператор>:=<Метка><:><Оператор>
<Оператор>:=<Присваивание>|<Вывод>
<Присваивание>:=<Переменная>=<Вычисленное выражение>
<Вывод>:=<PRINT>(<Вычисленное выражение>)
<Вычисленное выражение>:=<Expr2>
<expr2> := <expr1> {<бинарная, логическая><expr1>}
<expr1> := <expr> {<операция сравнения><expr>}
<expr> := <term>
{<add_op><term>}
<term> :=
<signed_factor>{<mul_op><signed_factor>}
<signed_factor> := [<add_op или лог-е отрицание>]<factor>
<factor> :=
<float>|”<string>”|(<expr2>)|<funcname>([<expr2>{,<expr2>}])
|<Переменная>
А как быть с оператором безусловного перехода? Модифицируем форму еще:
<Программа>:=<Поток операторов>
<Поток операторов>:=[<Общий оператор><;>]
<Общий оператор>:=<Помеч.оператор>|<Оператор>
<Помеч.оператор>:=<Метка><:><Оператор>
<Оператор>:=<Присваивание>|<Вывод>|<jump>|<пустой>
<jump>:=<GOTO><Метка><;>
<Пустой>:=<;>
<Присваивание>:=<Переменная>=<Вычисленное выражение>
<Вывод>:=<PRINT>(<Вычисленное выражение>)<;>
<Вычисленное выражение>:=<Expr2>
<expr2> := <expr1> {<бинарная, логическая><expr1>}
<expr1> := <expr> {<операция сравнения><expr>}
<expr> := <term>
{<add_op><term>}
<term> :=
<signed_factor>{<mul_op><signed_factor>}
<signed_factor> := [<add_op или лог-е отрицание>]<factor>
<factor> :=
<float>|”<string>”|(<expr2>)|<funcname>([<expr2>{,<expr2>}])
|<Переменная>
Если вы внимательно посмотрите, то увидите, что операторы вывода и перехода (и пустой тоже ) отличаются от оператора присваивания тем, что строку формы БН для первых завершает точка с запятой. И в программе происходит проверка на наличие точки с запятой. Без этого, как я пришел к выводу при экспериментах еще с первоначальным сканером (см.ранее), отлавливается меньше ошибок. Причина этого, скажу честно, сейчас мне не ясна. На этот счет см. также постскриптум к главе 9 (он дописан позже).
При дальнейшем программировании пришлось пойти на серьезное изменение программы – ввести глобальный параметр ToDo. Если он равен 0, то лексемы во время анализа в строку с обратной польской записью Res не вставляются, поиск переменных в таблицах не происходит, не запускается PostFix для вычисления значений формул. То-есть не выполняются никакие реальные полезные действия. В чем идея, спросите. В том, чтобы при безусловном переходе вперед, когда метка еще не найдена, отключать реальные действия (ToDo=0) , а когда метка найдена, снова включать. Можно было сперва просмотреть всю программу без выполнения (первый проход) и запомнить все метки, но мне хотелось сделать однопроходный транслятор.
Пришлось также ввести таблицу меток (также в виде 2-х списков – в первом имена меток, во втором «значени», то-есть смещения относительно начала строки с программой).
По правде говоря, с безусловными переходами пришлось долго повозиться.
Теперь – основные функции программы (те, которые в конце). Всю программу не привожу, так как она уже «разбухла» в достаточной степени.
/********************************/
void Expr2_Res() /* Не только разбор, но и вычисление выражения */
{
expr2();
/* Это если в конце разбора осталась лишняя
лексема (но не знак) */
if(*Lex!=0&&*Lex!=59) /* (null и ;) Попытка отловить если строка завершается на "левый" символ */
{
*Res=0;
ErrorLex(10);
/*WrStr("Unnecessary.txt",End,Lex,1);
*/
}
if(*Lex==0) /* Попытка отловить, если строка не завершается ; */
{
*Res=0;
ErrorLex(10);
/*WrStr("Unnecessary.txt",End,Lex,1);*/
}
if(ToDo!=0)
{
PostFix(Res,dest);
}
/**Res=0;*/
}
void Assignment() /* Это - (оператор) присваивания */
{
int N; /* Номер переменной в таблице */
int er;
if(*Lex>=65&&*Lex<=90) /* Если начинается с бол.лат.буквы */
{
if(ToDo!=0)
{
N=SEARCHlistP(pL1,Lex,0);
if(N<0)
{
er=ADDendStrP(pL1,Lex);
if(er!=1)
{
ErrorList(2);
return;
}
er=ADDendStrP(pL2,"N");
if(er!=1)
{
ErrorList(2);
return;
}
er=ADDendStrP(pL3,"Empty");
if(er!=1)
{
ErrorList(2);
return;
}
N=LenStrP(pL1)-1; /* Узнаем номер добавленной переменной в списке */
if(N<1)
{
ErrorList(3);
return;
}
}
}
NextLexema();
if(*Lex==61)
{
NextLexema();
Expr2_Res();
/* Результат теперь находится в dest */
if(*dest==0)
{
ErrorStack(-14);
return;
}
if(ToDo!=0)
{
if(*dest==34)
{ /* Если строка */
REPStrP(pL2,"S",N); /* Меняем тип на строковый */
REPStrP(pL3,dest,N); /* Помещаем результат в табл. пер-х */
}
else
{
if(testnum_p(dest)==0)
{
REPStrP(pL2,"D",N); /* Меняем тип на числовой (Double) */
REPStrP(pL3,dest,N); /* Помещаем результат в табл. пер-х */
}
else
{
ErrorStack(-15);
return;
}
}
}
*dest=0;
/**Res=0;*/
}
else
{
*Res=0;
ErrorLex(11);
/*WrStr("Error.txt",End,"ERROR - No = after var !!! ",1); */
return;
}
}
else
{
*Res=0;
ErrorLex(12);
/*WrStr("Error.txt",End,"ERROR - No var in beginning of
string !!! ",1); */
return;
}
}
void
OutPut()
{
NextLexema();
/***************/
if(*Lex==40) /* Если скобка */
{
NextLexema();
expr2();
if(ToDo!=0)
{
PostFix(Res,dest);
}
if(*Lex!=41)
{
*Res=0;
ErrorLex(14);
/*WrStr("Error.txt",End,"ERROR - No 2-nd bracket in
PRINT",1); */
return;
}
else
{
/*WrStr("ResultPRINT.txt",End,dest,1);*/ /* Это - главное
действие оператора */
if(ToDo!=0)
{
if(*dest==34)
{
printf("%s\n",dest+1);
}
else
{
printf("%s\n",dest);
}
}
*dest=0;
/**Res=0;*/
NextLexema();
if(*Lex!=59)
{
ErrorLex(15);
return;
/*WrStr("Error.txt",End,"No ; after PRINT(...)",1);
*/
/*NextLexema();*/
}
}
}
}
void
GoTo()
{
int N;
char S[20];
int J1;
char Lex1[50];
int er;
NextLexema();
if(ToDo!=0)
{
CpStr(Lex,Lex1);
}
/*WrStr("TEST.txt",End,"t
1",1);*/
NextLexema();
if(*Lex!=59)
{
WrStr("Error.txt",End,"No ; after GOTO ...",1);
NextLexema();
}
/*WrStr("TEST.txt",End,"t
2",1);*/
/***************/
if(ToDo!=0)
{
N=SEARCHlistP(pLa1,Lex1,0);
if(N<0)
{
CpStr(Lex1,WaitLabel); /* Не найденную в табл. метку записываем как ожидаемую (в будущем) */
ToDo=0;
}
else
{
er=READonPosStrP(pLa2,N,S);
if(er!=1)
{
ErrorList(4);
return;
}
J1=atoi(S);
/*J=J1+1;*/
/* *Lex=0;*/
pProg=Prog+J1;
}
}
}
void Operators() /* Это - собственно оператор (присваивания и др) */
{
if(COMPstr(Lex,"PRINT")==0)
{
OutPut();
return;
}
if(COMPstr(Lex,"GOTO")==0)
{
GoTo();
return;
}
if(COMPstr(Lex,";")==0) /*
Обработка пустого оп-ра */
{
return;
}
Assignment();
}
void
Labeled()
{
int N1;
char sL[50];
int er;
if(!(*Lex>=65&&*Lex<=90))
{
*Res=0;
ErrorLex(16);
/*WrStr("Error.txt",End,"Bad name of label ",1); */
/*goto EndLabeled;*/
return;
}
N1=SEARCHlistP(pLa1,Lex,0);
if(N1<0)
{
er=ADDendStrP(pLa1,Lex); /* Заносим метку в таблицу */
if(er!=1)
{
ErrorList(2);
return;
}
/*sprintf(sL,"%d%",(int)(pProg-Prog-lenstr(Lex)));*/
sprintf(sL,"%d%",pLab); /* Заносим, куда прыгать */
er=ADDendStrP(pLa2,sL);
if(er!=1)
{
ErrorList(2);
return;
}
if(COMPstr(Lex,WaitLabel)==0&&ToDo==0)
{
ToDo=1;
WaitLabel[0]=0; /* Очищаем ожидаемую метку - никуда не хотим идти */
}
NextLexema();
NextLexema();
Operators();
}
else
{
ErrorLex(17);
NextLexema();
NextLexema();
return;
/*goto EndLabeled;*/
}
/*EndLabeled:*/ ;
}
void GenOperators() /* Это - общий оператор (может быть помеченный, т.е. с меткой) */
{
char Lex1[150];
CpStr(Lex,Lex1);
NextLexema();
if(*Lex==58)
{
CpStr(Lex1,Lex);
pLab=(int)(pProg-Prog); /* Вычисляем, куда прыгать */
/*J--;*/
pProg=pProgLast;
Labeled();
}
else
{
CpStr(Lex1,Lex);
/*J--;*/
pProg=pProgLast;
Operators();
}
}
/*****/
void StreamOp() /* Это - поток операторов */
{
while(*Lex!=0)
{
GenOperators();
/*NextLexema();*/
if(*Lex!=59)
/* не нашли (;) */
{
*Res=0;
ErrorLex(13);
/*WrStr("Error.txt",End,"ERROR - No ; in the end of
string !!! ",1); */
return;
}
else
{
NextLexema();
}
}
}
void Prog0() /* Эта ф-я соответствует программе в целом */
{
int er;
ToDo=1;
/*pSt=CreateStack(10000);*/
/* Заводим во все списки первое,пустое значение */
pL1=CRbegStrP(""); /* Здесь имена переменных */
if(pL1==NULL)
{
ErrorList(1);
return;
}
pL2=CRbegStrP(""); /* Здесь их типы */
if(pL2==NULL)
{
ErrorList(1);
return;
}
pL3=CRbegStrP(""); /* А здесь их значения */
if(pL3==NULL)
{
ErrorList(1);
return;
}
pLa1=CRbegStrP("");
if(pLa1==NULL)
{
ErrorList(1);
return;
}
pLa2=CRbegStrP("");
if(pLa2==NULL)
{
ErrorList(1);
return;
}
StreamOp();
/*DeleteStack(pSt);*/
DELallStrP(pL1);
DELallStrP(pL2);
DELallStrP(pL3);
DELallStrP(pLa1);
DELallStrP(pLa2);
}
/*******************/
void
main(void)
{
Lex[0]=0;
Res[0]=0;
End[0]=13;
End[1]=10;
End[2]=0;
DelimPol[0]=4; /* Задаем разделитель в стеке */
DelimPol[1]=0;
/* Чтение файла с программой */
/*****/
FILE *f1;
int Ch;
char ch;
int jc,J;
int er;
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();
Prog0();
/*
er=CpListToFileP(pL1,"ListVar.txt");
if(er!=0)
{
ErrorList(5);
return;
}
er=CpListToFileP(pL3,"ListVar_Val.txt");
if(er!=0)
{
ErrorList(5);
return;
}*/
/* er=CpListToFileP(pL2,"ListVar_Type.txt");
if(er!=0)
{
ErrorList(5);
return;
}*/
/*
er=CpListToFileP(pLa1,"ListLab.txt");
if(er!=0)
{
ErrorList(5);
return;
}
er=CpListToFileP(pLa2,"ListLab_Val.txt");
if(er!=0)
{
ErrorList(5);
return;
}*/
return;
}
Это все – из папки Gramm94_002.