Интерпретация оператора switch требует больше труда, чем обработка оператора if. Одна из причин кроется в более сложном синтаксисе. Другая состоит в том, что оператор switch зависит от оператора break. Следовательно, обработка оператора switch влечет за собой также интерпретацию оператора break. Далее обсуждаются оба. Действительно, обработка оператора break довольно легка, потому что он выполняется одинаково: независимо от того, где используется, в операторе switch или в цикле, break вызывает завершение блока, связанного с указанными операторами. Интерпретатор Mini С++ обрабатывает оператор break с помощью глобальной переменной-флага breakfound, которая первоначально равна false. Когда обнаружен оператор break, переменной breakfound присваивается значение true. Затем эта переменная проверяется при обработке оператора switch (и описанных далее операторов цикла), для того чтобы выяснить, выполнялся ли оператор break. Если переменная breakfound равна true, выполнение текущего блока завершается и ей присваивается значение false. Оператор break интерпретируется в функции interp () с помощью следующего кода: case BREAK: // обрабатывает break breakfound = true; // Восстанавливает вложенную область видимости. local_var_stack.resize(nest_scope_stack.top()); nes t_scope_s tack.pop(); return; Как видите, кроме присваивания значения true переменной breakfound восстанавливается стек локальных переменных до состояния, которое он имел перед началом выполнения прерываемого блока. Напоминаю, что внутри любого блока могут быть объявлены переменные, локальные по отношению к нему. Следовательно, когда встречается оператор break, все ло-. кальные переменные прерываемого блока должны быть удалены. Оператор switch выполняется функцией exec_switch(), код которой приведен далее. // Выполняет оператор switch. void exec_switch() { int sval, cval; int brace; eval_exp(sval); // Получает выражение switch. // Проверяет начало блока. if(*token != '{') throw InterpExc(BRACE_EXPECTED); // Записывает новую область видимости. nest_scope_stack.push(local_var_stack.size()); // Теперь проверяет операторы case. for(;;) { brace = 1; // Находит оператор case, do { get_token(); if(*token == '{') brace++; else if(*token == '}') brace—; } while(tok != CASE && tok != END && brace); // Если соответствующего оператора case не найдено, то пропускает, i f(!brace) break; if(tok == END) throw InterpExc(SYNTAX); // Получает значение метки оператора case. eval_exp(cval); // Читает и отбрасывает ":" get_token () ; if(*token != ':') throw InterpExc(COLON_EXPECTED); // Если значения совпадают, то интерпретирует, if(cval == sval) brace = 1; do { interp(); if(*token == '{') brace++; else if(*token == '}*) brace—; } while(!breakfound && tok != END && brace); // Находит конец оператора switch, while(brace) { get_token(); if(*token == '{') brace++; else if(*token == '}') brace—; } breakfound = false; break; } } } Сначала функция exec_switch() получает значение выражения оператора switch и запоминает его в переменной sval. Затем она находит начало блока switch и сохраняет вершину стека локальных переменных в стеке nest_scope_stack. Этот шаг необходим, потому что оператор switch создает вложенную область видимости. Далее исследуются значения меток операторов case, пока не встретится то, которое совпадает со значением переменной sval, или не будет достигнут конец оператора switch (напоминаю, что для простоты реализации интерпретатор Mini С++ не поддерживает метку default в операторе switch). Если совпадение найдено, выполняются операторы, связанные с этой меткой, до тех пор, пока не встретится оператор break (т. е. пока переменная breakfound не равна true) или пока не будет найден конец блока оператора switch. После обнаружения оператора break функция exec_switch() завершается поиском конца блока оператора switch и установкой значения переменной breakfound равным false.
|