Наверх

Пред. След.

 

 

WHILE.

 

С этим оператором пришлось много повозиться. Ввел даже еще одну отключающую реальные действия (то-есть вычисления) переменную ToDoW.  Кроме того я пришел к выводу, что внутри функции WhileOp лучше вызывать сразу поток операторов, а не перебирать операторы в цикле. Потом в этом же ключе переделал и IfOp. Вот отрывок и с WHILE и с IF:

/*** If  **/

            void IfOp()

            {

                        int Result;

                        int ToDoif1;

                        char Lex2[20];

 

 

                        NextLexema();

                        /***************/

                if(*Lex!=40)  /* Если скобка */

                        {

                        *Res=0;

                        ErrorLex(18);

        /*WrStr("Error.txt",End,"ERROR - No ( after IF",1);*/

                        return;

                        }

                        ToDoif1=ToDoif;

 

                        NextLexema();

 

                        expr2();

 

                        if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)

                        {

        PostFix(Res,dest);

        Result=atoi(dest); 

                        }

 

                        if(*Lex!=41)

                        {

                        *Res=0;

                        ErrorLex(19);

        /*WrStr("Error.txt",End,"ERROR - No 2-nd bracket in IF",1);*/

                        return;

                        }

 

                        if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)

                        {

                        if(Result!=0)

                        {

                                    ToDoif=1;

                        }

                        else

                        {

                                    ToDoif=0;

                        }

                        }

 

                        NextLexema();

                        StreamOp();

        /*WrStr("Lex0.txt",End,Lex,1);*/

                        ToDoif=ToDoif1;

 

 

/****ELSE******/

                        if(COMPstr(Lex,"ELSE")==0)

                        {

        if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)

                        {

                        if(Result!=0)

                        {

                                    ToDoif=0;

                        }

                        else

                        {

                                    ToDoif=1;

                        }

                        }

 

                        NextLexema();

                        StreamOp();

 

                        ToDoif=ToDoif1;

 

        return;

 

 

                        }

/*****конец обр-ки ELSE*******/

                        else

                        {

                        ToDoif=ToDoif1;

        return;

                        }

 

            /***************/

            }

 

/*** End of If  **/

 

/*** While  **/

            void WhileOp()

            {

 

                        int Result1=0;

                        int ToDoW1;

                        int iAdr;

                        char sAdr[100];

                        int iSTR1;

                        int ToExit=0; /* Если 0, то возвращаться к началу цикла */

        int iAdrExit;

                        char sAdrExit[100];

 

                        int iSTR2;

                        int res;

 

                        ToDoW1=ToDoW;

 

                        while(ToExit==0)

                        {

                NextLexema();

                        /***************/

                if(*Lex!=40)  /* Если не скобка */

                        {

                        *Res=0;

        WrStr("Error.txt",End,"ERROR - No ( after WHILE",1);

        WrStr("Error.txt",End,Lex,1);

 

                        return;

                        }

 

                        NextLexema();

 

                        expr2();

                        if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)

                        {

        PostFix(Res,dest);

        Result1=atoi(dest);

                        *Res=0;

                        *dest=0;

                        }

                        else

                        {

                                    *Res=0;

                                    *dest=0;

                                    Result1=0;

                        }

 

                        if(*Lex!=41)

                        {

                        *Res=0;

        WrStr("Error.txt",End,"ERROR - No 2-nd bracket in WHILE",1);

                        return;

                        }

 

                        if(Result1!=0)

                        {

                                    ToDoW=1;

                                    ToExit=0;

                        }

                        else

                        {

                                    ToDoW=0;

                                    ToExit=1;

                        }

 

                        NextLexema();

                        StreamOp();

 

                        if(ToDoW==0)

                        {

                                    ToExit=1;

                        }

 

                        if(!(ToDo!=0&&ToDoif!=0&&ToDoW!=0))

                        {

                                    ToExit=1;

                        }

 

        res=GetArrayFromStack(pStW,sAdr,100);

                        if(res!=100)

                                    {

                        ErrorStack(-7);

                                    }

        iAdr=atoi(sAdr);

 

                        if(ToExit==0)

                        {

                                    res=AddArrayToStackNoPart(pStW,sAdr,100);

                                    if(res!=100)

                                    {

                        ErrorStack(-3);

                                    }

 

                pProg=Prog+iAdr;

                        continue;

                        }

                        else

                        {

                        ToDoW=ToDoW1;

                        break;

                        }

 

                        };

                        /*pProg=pProgLast;*/

 

                        NextLexema();

 

                        /***************/

            }

 

/*** End of While  **/

 

    void Break()

            {

        if(ToDo!=0&&ToDoif!=0&&ToDoW!=0)

                        {

                                    iBreak=1;

                        }

                        return;

            }

 

    void Operators()  /* Это - собственно оператор (присваивания и др) */

            {

                        int iAdr0;

                        char sAdr0[102];

                        int res;

                        if(COMPstr(Lex,"PRINT")==0)

                        {

                                    OutPut();

                                    return;

                        }

                        if(COMPstr(Lex,"GOTO")==0)

                        {

                                    GoTo();

                                    return;

                        }

                        if(COMPstr(Lex,";")==0) /* Обработка пустого оп-ра */

                        {

                                    return;

                        }

                        if(COMPstr(Lex,"IF")==0)

                        {

                                    IfOp();

                                    return;

                        }

                        if(COMPstr(Lex,"WHILE")==0)

                        {

                        iAdr0=(int)(pProg-Prog); /* Вычисляем, куда возвращаться */

                        sprintf(sAdr0,"%d",iAdr0);

                                    res=AddArrayToStackNoPart(pStW,sAdr0,100);

                                    if(res!=100)

                                    {

                        ErrorStack(-3);

                                    }

                        WhileOp();

                        return;

                        }

                        if(COMPstr(Lex,"ENDWHILE")==0)

                        {

                        /*iAdr0=(int)(pProg-Prog);

                        sprintf(sAdr0,"%d",iAdr0);

                                    res=AddArrayToStackNoPart(pStW,sAdr0,100);

                                    if(res!=100)

                                    {

                        ErrorStack(-3);

                                    }*/

                                    return;

                        }

                        Assignment();

            }

Обратим внимание, что создан еще один стек – для адресов возврата в цикле WHILE. И функция operators при нахождении ключевого слова WHILE вычисляет и заносит в стек адрес возврата.

Здесь мне пришлось столкнуться с весьма неприятным эффектом – безусловные переходы плохо живут с оператором WHILE. Если есть два вложенных цикла, и из внутреннего пытаемся выйти вперед с помощью GOTO, результат окажется совсем не таким, на какой мы рассчитывали. Пример программы (на разрабатываемом нами языке):

M=5;

WHILE(M>0)

M=M-1;

PRINT(M);

J=5;

WHILE(1>0)

J=J-1;

IF(J<2)

GOTO L1;

ENDIF;

PRINT("SSSSSS");

ENDWHILE;

L1: H=10;

ENDWHILE;

L2:PRINT("End"+"  !!!");

 

В принципе, в этом нет ничего необычного. Сошлюсь на литературный источник – книгу «Программирование в среде Turbo Pascal 7.0» А.М.Епанешникова и В.А.Епанешникова:

«Безусловный переход может осуществляться далеко не из каждого места программы и не в любое место программы. Так, нельзя с помощью этого оператора перейти из основной программы в подпрограмму или выйти из подпрограммы, не рекомендуется осуществлять переход внутрь структурированного оператора, т.к. он может дать неправильный результат и т.д..». Добавлю, что в языке Паскаль в число структурированных операторов входят IF,CASE,REPEAT,WHILE,FOR и др.

 

Есть еще одна тонкость. В существующем варианте после ENDWHILE и ENDIF не обязательно проставлять точку с запятой. Но вот пример (опять с двумя вложенными WHILE), который отрабатывает немного не так, как бы хотелось (в каждом цикле захватывется оператор PRINT и печатается End!).

V=3;

WHILE(V>0)

V=V-1;

PRINT("AAAAAAA "+DblToStr(V));

H=6;

WHILE(H>0)

PRINT("BB"+DblToStr(H));

H=H-1;

 

ENDWHILE

 

ENDWHILE

;;;

L1:PRINT("End!");

;;;;

 Если же поставить ;  после ENDWHILE (первого), то все будет как надо. Таким образом, лучше предусмотреть проверку на наличие ; после ENDWHILE и ENDIF – так будет надежнее.

В папке Gramm95_016 есть уже не только BREAK, но и оператор CONTINUE. Из циклов WHILE нужно выходить с их помощью (или естественным образом – когда выражение в заголовке цикла станет нулевым).

Вот нормально отрабатываемый пример (внешний цикл завершается естественным образом, а внутренний – по Break):

V=3;

WHILE(V>0)

V=V-1;

PRINT("AAAAAAA "+DblToStr(V));

H=6;

WHILE(1>0)

PRINT("BB"+DblToStr(H));

H=H-1;

 

IF(H<3)

BREAK;

ENDIF;

 

ENDWHILE;

 

ENDWHILE;

;;;

L1:PRINT("End!");

;;;;

Также отмечу, что во всех предыдущих проектах я забыл ввести проверку деления на 0. Но это легко добавить.

В заключение этой главы – форма Бэкуса-Наура:

            <Программа>:=<Поток операторов>

            <Поток операторов>:=[<Общий оператор>]

            <Общий оператор>:=<Помеч.оператор>|<Оператор>

            <Помеч.оператор>:=<Метка><:><Оператор>

            <Оператор>:=<Присваивание>|<Вывод>|<jump>|<пустой>|<IfOp>

|<WhileOp>|<Break>|<Continue>

            <IfOp>:=IF(<Вычисленное выражение>)<Поток операторов>[

ELSE<Поток операторов>]ENDIF

            <WhileOp>:=WHILE(<Вычисленное выражение>)<Поток операторов>ENDWHILE

            <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>}])

|<Переменная>

 

            Здесь после ENDIF и ENDWHILE нет  <;>, но, как я уже сказал выше, лучше ввести это требование. В дальнейшем это будет сделано.

 

Хостинг от uCoz