Вызов
функций.
Перехожу к очень важному моменту – как запрограммировать вызов функции (не встроенных в язык, а тех, которые программируются на самом создаваемом языке). Пришлось внести в программу довольно большие изменения. В частности, пришлось создать списки для таблицы функций. В отдельных списках хранятся:
1.Названия
2.Адрес функции (в нашем случае это смещение по тексту программы).
3.Количество параметров
4.Строка с именами параметров (где они перечислены через пробел).
Кроме того пришлось создать стек для значений параметров вызываемых функций, возвращаемых значений и адресов возврата.
Был также введен оператор START. При начале прохода программы переменные, которые включают реальное выполнение действий (ToDo,ToDOif,ToDoW) нулевые (а не единичные, как раньше), и лишь когда встретится START , они становятся равными единице (включается реальное выполнение). А до этого лишь анализируются встречающиеся функции, имена их и адреса заносятся в таблицу и так далее. В соответствии с этим тела функций должны быть расположены в начале программы, потом должен идти оператор START, далее – основная программа.
Оператор RETURN закладывает в стек вычисленное функцией значение.
Форму БНФ я не привожу (при программировании вызовов функций я ей уже не пользовался).
Вот измененная функция fact, которая стала весьма громоздкой. Именно здесь происходит вызов функции, а перед этим занесение в стек параметров и адреса возврата.
void fact() /* Это множитель - конечный пункт рекурсии */
{
int
iF;
int
N1;
int
er;
char
ResNum[101];
char
ResF1[50];
char
Lex_1[50];
*ResF1=0;
*Lex_1=0;
char
St1[102];
int
res;
int
iAdr0;
int
iAdr;
if(*Lex==40)
{
NextLexema();
expr2();
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)) /* Проверка - является ли действ. числом */
{
if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)
{
StrCat(Lex,Res);
StrCat(DelimPol,Res); /* Добавляем разделитель в стеке */
}
NextLexema();
}
else
{
if(*Lex==34) /* Кавычка */
{
if(lenstr(Lex)<100) /* Чересчур длинная лексема */
{
if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)
{
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
{
if(*Lex>=65&&*Lex<=90) /* Переменная или функция */
{
StrCat(Lex,ResF1);
StrCat(Lex,Lex_1);
NextLexema();
if(*Lex==40) /* Значит похоже на функцию */
{
/* Сравниваем с именами существ. функций */
if(COMPstr(ResF1,sFunc1)==0)
{
iF=1;
goto
endSF;
}
if(COMPstr(ResF1,sFunc2)==0)
{
iF=1;
goto
endSF;
}
if(COMPstr(ResF1,sFunc3)==0)
{
iF=1;
goto
endSF;
}
if(COMPstr(ResF1,sFunc4)==0)
{
iF=2;
goto
endSF;
}
if(COMPstr(ResF1,sFunc5)==0)
{
iF=2;
goto
endSF;
}
if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)
{
N1=SEARCHlistP(pLF1,ResF1,0); /* Ищем
имя в 1-й таблице функций*/
if(N1<0)
{
*Res=0;
ErrorLex(26); /* Не найдено имя функции */
return;
}
er=READonPosStrP(pLF2,N1,ResNum); /* Ищем смещение во 2-й таблице (списке) */
if(er!=1)
{
ErrorList(4);
return;
}
iAdr=atoi(ResNum);
*ResNum=0;
er=READonPosStrP(pLF3,N1,ResNum);
/* Ищем число пар-в в 3-й таблице (списке) */
if(er!=1)
{
ErrorList(4);
return;
}
iF=atoi(ResNum);
*ResNum=0;
/*****/ /* Обработка вызова внешней ф-ии */
NextLexema();
if(iF>0)
{
expr2();
PostFix(Res,dest);
CpStr(dest,St1);
res=AddArrayToStackNoPart(pStF,St1,100);
if(res!=100)
{
ErrorStack(-3);
}
*Res=0;
*dest=0;
}
while(iF>1)
{
iF--;
if(*Lex!=44)
{
ErrorLex(6); /* Нет запятой в функции */
return;
}
NextLexema();
expr2();
if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)
{
PostFix(Res,dest);
CpStr(dest,St1);
res=AddArrayToStackNoPart(pStF,St1,100);
if(res!=100)
{
ErrorStack(-3);
}
*Res=0;
*dest=0;
}
}
if(*Lex!=41)
{
WrStr("error.txt",End,"Made...",1);
WrStr("error.txt",End,Lex,1);
ErrorLex(7); /* Нет 2-й скобки в функции (или слишком много пар-в) */
return;
}
else
{
iAdr0=(int)(pProg-Prog); /* Вычисляем, куда возвращаться */
sprintf(St1,"%d",iAdr0);
res=AddArrayToStackNoPart(pStF,St1,100);
if(res!=100)
{
ErrorStack(-3);
}
pProg=Prog+iAdr; /* Перемещаемся по тексту программы к функции */
FuncStreamOp(ResF1); /* Вызов */
res=GetArrayFromStack(pStF,St1,100);
if(res!=100)
{
ErrorStack(-7);
}
StrCat(St1,Res); /* Записываем в строку результата */
StrCat(DelimPol,Res);
}
goto endSF2;
;
}
else
{
while(1)
{
NextLexema();
if(*Lex==41)
{
NextLexema();
goto endSF2;
}
}
}
/*****/ /* Конец обработки вызова внешней ф-ии */
endSF:
NextLexema();
if(iF>0)
{
expr2();
}
while(iF>1)
{
iF--;
if(*Lex!=44)
{
ErrorLex(6); /* Нет запятой в функции */
return;
}
NextLexema();
expr2();
}
if(*Lex!=41)
{
WrStr("error.txt",End,"Inline...",1);
WrStr("error.txt",End,Lex,1);
ErrorLex(7); /* Нет 2-й скобки в функции (или слишком много пар-в) */
return;
}
else
{
if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)
{
StrCat(ResF1,Res); /* пишем в результ.строку имя функции */
StrCat(DelimPol,Res);
}
*Lex=0;
NextLexema();
}
endSF2: ;
;
}
else
{
/****/
/* Это просто переменная */
if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)
{
N1=SEARCHlistP(pL1,ResF1,0); /* Ищем
имя в 1-й таблице (списке) функций */
if(N1<0)
{
*Res=0;
ErrorLex(8);
return;
}
er=READonPosStrP(pL3,N1,ResNum);
/* Ищем значение в 3-й таблице (списке) */
if(er!=1)
{
ErrorList(4);
return;
}
if(*ResNum==0)
{
*Res=0;
ErrorLex(9);
return;
}
/****/
StrCat(ResNum,Res); /* Записываем в строку результата */
StrCat(DelimPol,Res);
}
}
}
else
{
*Res=0;
/*sprintf(Res,"Lexema:
%d ",J-1);
StrCat("ERROR
- Wrong lexema! ",Res);*/
ErrorLex(3);
}
}
}
};
};
}
А вот обработка определения функции (еще когда не включено выполнение):
/* Обработка определения функции */
void FuncOp()
{
int
er;
int
iPar=0;
int
iAdr;
char
FPar[500];
*FPar=0;
NextLexema();
er=ADDendStrP(pLF1,Lex); /* Заносим имя функции в
таблицу */
if(er!=1)
{
ErrorList(2);
return;
}
NextLexema();
/***************/
if(*Lex!=40) /* Если не скобка */
{
*Res=0;
ErrorLex(27);
return;
}
while(iPar>-1)
{
NextLexema();
if(*Lex>=65&&*Lex<=90)
{
iPar++;
StrCat(Lex,FPar);
StrCat(" ",FPar);
}
else
{
ErrorLex(28);
return;
}
NextLexema();
if(*Lex==41)
{
break;
}
if(*Lex==44)
{
continue;
}
ErrorLex(28);
return;
}
er=ADDendStrP(pLF4,FPar); /* Заносим имя функции в
таблицу */
if(er!=1)
{
ErrorList(2);
return;
}
sprintf(FPar,"%d",iPar);
er=ADDendStrP(pLF3,FPar); /* Заносим имя функции в
таблицу */
if(er!=1)
{
ErrorList(2);
return;
}
iAdr=(int)(pProg-Prog);
/* Вычисляем, куда переходить */
sprintf(FPar,"%d",iAdr);
er=ADDendStrP(pLF2,FPar); /* Заносим имя функции в
таблицу */
if(er!=1)
{
ErrorList(2);
return;
}
NextLexema();
StreamOp();
}
/* Конец обработки определения функции */
А теперь – выполнение функции после ее вызова. Нужно прежде всего достать из стека значения переменных (а также адрес возврата, поскольку он ближе всего):
/* Обработка вызова ф-ии (самодельной) */
void FuncStreamOp(char *Name)
{
int
N1,iP,i,er,res,N2;
char
StrP[500];
char
StrP1[100];
char
StP[102];
int
Adr;
res=GetArrayFromStack(pStF,StP,100);
if(res!=100)
{
ErrorStack(-7);
}
Adr=atoi(StP);
N1=SEARCHlistP(pLF1,Name,0); /* Ищем
имя в 1-й таблице функций*/
if(N1<0)
{
*Res=0;
ErrorLex(29); /* Не найдено имя функции при вызове */
return;
}
er=READonPosStrP(pLF3,N1,StrP); /* Ищем кол-во пар-в в 3-й таблице (списке) */
if(er!=1)
{
ErrorList(4);
return;
}
iP=atoi(StrP);
er=READonPosStrP(pLF4,N1,StrP); /* Ищем имена пар-в в 4-й таблице (списке) */
if(er!=1)
{
ErrorList(4);
return;
}
i=iP;
while(i>0)
{
CpWordN(StrP,StrP1,32,i);
i--;
res=GetArrayFromStack(pStF,StP,100);
if(res!=100)
{
ErrorStack(-7);
}
N2=SEARCHlistP(pL1,StrP1,0); /* Ищем
имя в 1-й таблице функций*/
if(N2<0)
{
er=ADDendStrP(pL1,StrP1);
if(er!=1)
{
ErrorList(4);
return;
}
er=ADDendStrP(pL2,"N");
if(er!=1)
{
ErrorList(4);
return;
}
er=ADDendStrP(pL3,"Empty");
if(er!=1)
{
ErrorList(4);
return;
}
N2=SEARCHlistP(pL1,StrP1,0);
}
/***/
if(*StP==34)
{ /* Если строка */
REPStrP(pL2,"S",N2); /* Меняем тип на строковый */
REPStrP(pL3,StP,N2); /* Помещаем результат в табл. пер-х */
}
else
{
if(testnum_p(StP)==0)
{
REPStrP(pL2,"D",N2); /* Меняем тип на числовой (Double) */
REPStrP(pL3,StP,N2); /* Помещаем результат в табл. пер-х */
}
else
{
ErrorStack(-15);
return;
}
/****/
}
}
NextLexema();
StreamOp();
pProg=Prog+Adr;
/* Перемещаемся по тексту программы назад*/
NextLexema();
}
Все эти занесения в стек и вынимания оттуда требуют весьма тщательного и аккуратного программирования. Но все тем не менее заработало. Вот входной пример (на разрабатываемом языке) с вызовом двух функций.
FUNCTION
A1(R,D,D1)
;
PRINT (
"AAAAAAAAAAA");
R1=A2("function");
PRINT(R1);
PRINT(R);
PRINT(D);
PRINT(D1);
PRINT("BBBBBBBBBBB");
;
RETURN(100);
ENDFUNCTION
;
FUNCTION
A2(HHHH);
PRINT(HHHH);
G=5;
WHILE(1=1)
G=G-1;
PRINT("+++");
IF(G=3)
BREAK;
ENDIF;
ENDWHILE;
RETURN(HHHH+" Good!");
PRINT("$$$$$$$$$$$$$$$$$$$$$");
ENDFUNCTION
;
PRINT("Out!!!");
START
A=1;
PRINT( A);
IF(0)
E=5;
PRINT("Inside
if");
E=A1(4.5,100,-20);
ENDIF;
PRINT("AFTER");
PRINT(E);
В программе уже более трех тысяч строк (не считая библиотек со строковыми функциями, с обработкой стека и списков, в которых хранятся таблицы). Мне уже иногда кажется, что она живет какой-то своей жизнью, поскольку обозреть и осмыслить ее всю уже весьма затруднительно. И иногда на чистой интуиции находится решение – если есть какой-то глюк, добавляем в каком-то (интуитивно найденном) месте вызов сканера (NextLexema();) – и все налаживается.