Г. Шилдт - Полный справочник по C++ (1109478), страница 145
Текст из файла (страница 145)
всгсру(сокол, секр сохеп)г сои суре .= Ьсок суре; Часть У. Приложения иа языке С++ е1ве ( дех СоКеп(); // Извлекаем следуют(ую часть зьюажения ехр. еъа1 ехр2(геяи1г); чатя(я1ос) = теви1с; гесигп; ) ече1 ехр2(теяи1С) ) Очевидно, что эта функция предварительно просматривает выражение, чтобы определить, действительно ли следует выполнить присваивание.
Это необходимо делать потому, что имя переменной всегда предшествует опера~ору присваивания, цо само по себе не гарантирует, что за ним обязательно следует оператор присваивания. Иначе говоря, анализатор распознает выражение А=100 как операцию присваивания и может отличить его от выражения А/1О. Для етого функция ечаз. ехрз() считывает из входного потока следующую лексему. Если она не содержит знака равенства, лексема возвращается во входной поток с помощью функции риеьаскО, которая является частью класса ратвех. Возвращает лексему во входной соток. чотй рахяет::рисЬасК() ( сцат *С; токеп: Еог(( "с; с>+) ехр рсх--; После сделанных изменений, класс рахвех принимает следующий вид. /* Программа, выполняющая рекурсивный нисходящий анализ выражений, содержащих переменные.
*/ Вапс1ийе <Ьавтхеащ> агпс1ийе <сягй1гЬ> Этпс1ийе <сстуре> $1пс1ийе <сясгтпд> ивйпд пащеярасе всй; епищ Сурев ( ПЕЬ|МХтЕЕ = 1, ЧЛЕ1ЛВЕЕ, МПМВЕВ); сопят гпт Н(ЛГ/ЛПЕ = 2гы с1аяя ратвег сЬат +ехр рхт; // осыпается ня выражение. сЬах сокеп(вс); // Хранит текущую лексему. сЬат соК суре; // Хранит тип лексемы. йоиЬ1е чадя(НЦМ(/ЛРБ]; // Хранит значения переменных. чогй еча1 ехр1(йоиЬ1е агеяи1с) ( чогй еча1 ехр2(йоибзе атеви1Х); чогй еча1 ехрЗ(йоиЬ1е ьгеви1с); чотй еча1 ехр4(йоиЬ1е атеви1с)( чогй еъа1 ехр5(йоиЬ1е атеви1с)( чо1й еча1 ехрб(йоиЬ1е атеяи1с); Глава 40. Синтаксический анализ выражений чо1с) агом(г)оиЬ1е ьгевп1г); чо1г) оег го)геп(); чо1с) рпсЬас)г(); чоЫ ветгот(апг еггог); с)опЬ1е Ыпг) чаг(слаг *в); 1пг 1вг)е1хи(слаг с); риЬ11с: рагвег(); г)опЬ1е еча1 ехр(сЬаг *ехр); ): Конструктор класса рагвег.
ратвег::рагвет() 1пг 1; ехр Гтг = Г)ОЫ„. йот(1=0; 1<ИЖЧМБ; 1++) чагв [11 = 0.0; ) // Отправная точка анализа. с)опЬ1е ратвет::еча1 ехр(сЬат *ехр) ( боиЬ1е тево1т; ехр рст = ехр; оег го)геп(): 11() то)геп) ( вегтот(2); // Выражение пусто. гесптп 0.0; ) еча1 ехр1(гевп1т)г 11(*со)геп) веггот(0); // последняя лексема должна оьгть // нулевым символом. тегигп геви1с," ) // Врисваивание.
чоМ рагвет:;еъа1 ехр1(бооЬ1е атевп1г) ( апс в1ос/ сьат гто)г гуре; сЬаг сеир то)сел[80); 11(ГОК Гуре==удрх)(ВЬЕ) ( // Сохраняем старую лексему. вгтсру(геюр гоЕеп, со)геп); ггоК гуре = го)г туре; // Вычисляем индекс переменной. в1от = Гопррет(*то)геп) — 'А'; оет соКеп(); Н (*со)геп != '=') Часть Ч. Приложения иа языке С++ рисЬаск(); // Возвращаем текущую лексемУ.
// Восстанавливаем старую лексему// присваивание не выполняется. астору(со)сеп, сещр го)сеп); со)с суре = тсо1с гуре; е).ве дес токеп(]; // Извлекаем следующую часть выражения ехр. еча1 ехр2(геви1с); ъатв(в1ос] = геви1с; гесигп; еча1 ехр2(теви1с) // Складываем ияи вычитаем два терма. чо1т) ратвет::еча1 ехр2(т)оиЬ1е агеяи1с) ( гед1вгег онат ор; боиЬ1е сещр; еуа1 ехрЗ(теви1С); ий11е<[ор = *тохеп) == '+' /~ ор == '-') оег сохеп(); еуа1 ехрЗ[сещр); ви1тсЬ[ор) ( саяе геви1С = геви1С - Сещр; Ьтеани саве '~.': геви1С = геви1С е Сещр; Ьгеа)с; ) ) ) // Умножаем или делим два Фактора.
чоЫ ратвет::еча1 ехрЗ(с)оиЪ1е атеви1с) гео1всег сЬат ор; йоиЬ1е секр; еча1 ехр4 [теви1т); ыЬ11о((ор = "сокеп) == '*' ] ] ор =-= '/' ] ( ор == 'в') ( дес соЕеп(); еча1 ехр4(сещр); ви1гсЬ(ор) ( саве '*': теви1с = геви1с * сещр; Ьтеа)т) саяе теви1с = теви1с / сещр; Ьгеанщ саяе 'Ъ': теви1т = (1пс) теви1г Ъ (1пс) сещр; Глава 40. Синтаксический анализ выражений Ьгеаки // Возведение в степень. чс(б рагвег::еча1 ехр4(с(оцЬ1е ьгевц1к) бсцЬ1е сеыр, ех; ге01всег Впс (:; // Выполнение унарных операций в или — . чсЫ рагвег::еча1 ехр5(с(оцЬ1е ьгевц1г) герьвгег оиаг ор; ор = 0; 10((Сок Гуре == ВЕВХИХТЕЕ) ЬЬ *Гокеп= †.
'+' ор = *гоКеп; Вес со)сеп(] ) ) еча1 ехрб(гевц10); гб(ор==' †') гезц1с = -гевц10; ) *го]сеп == '-') ( // Обработка вьрахениз, содеркапего скобки. чоЫ рагвег::еъа1 ехрб(боцЬ1е агевц10) ( 1б((*сойеп == '(')) ВЕГ Сс]ЪЕП(); еча1 ехр2(гевп1к); 10('го)ъеп '.=- ')") веггог(1); деь,ьойеп(]) 1 е1ве акое(гевц1г); ) // Извлекает число или значение переменной. чо10 рагвег::ага(боцЬ1е Ьгевц1Г) ви10сЬ(вон суре) ( саве (/ВЕ1АВЕЕ: гевц1г = Вянем чаг(ьскеп); Часть ().
Приложения на языке С++ еъа1 ехр5(гевц10]; 10("сойеп== '"') Век Гокеп(); еча1 ехр4(гетр); ех = гевц1с; 10(сеир==0.0) ( гевц1к = 1.0; геспгп; ) ВОГ(Г=(1ПГ) ГЕтр-1; К>0; /-Ь) Гвзц1Ь = ГЕВц1Г к (ВОЦЬ1Е) ЕХ; ) дес со)пел[!: гесцгп; саве )(ПЕВЕВ: тевц1г = агой(гс)сеп)) дет тоиеп(); гесцгп; п)ейац1т: веттог(0); (/ Возвращает лексему во входной готок. чс1п) ратвет::рцг)пас)п() снах яс; го)сеп; Ест(; *г; с++) ехр рсг--; // Выводит на экран сообщение о синтаксической ошибке. чо(б ратвет:".вегтот(1пт етгот) всаг1с снах е(]= ( "Синтаксическая ошибка "Нарушен баланс скобок", "Выражение пусто" ); сост « е(етгот] « епп)1) // Получает следующую лексему.
чоаг) ратвег.":дес го)пеп() гедаэгег сЬаг *гешр; го)п суре = 0; гещр =. го)пеп) *гетр = '10'; ВВ(!*ехр рог) гетцтп; // В конце выражения. ин11е(1вврасе(*ехр рог)) ++ехр рот; // пропуск раэлелителя. 10(вттсЬт("+-"(Ъ"=()", *ехр рот))( Го)п Суре = ПЕПХИ1ТЕви (/ Переход к следующему символу. *Сещр+е = *ехр роте+; ) е1ве 1В(1эа1рЬа(*ехр ргт)) ин11е()авп)е11ш(*ехр ртг)) *гетр++ =. *ехр рогач; Го)п Гуре = УАРГАВПЕ; ) е1яе И(1вп)адат(яехр ртт) ) ( ии11е(!Ввбе1мп(*ехр рог) ) *гетр+т = "ехр рот++) Го)п Гуре = ИПИВЕГИ ) Глава 40.
Синтаксический анализ выражений "семр = '(О' Если параметр с является разделителем, // воззражает значение Стае. 1лс рссяет::(яс)е11з(снах с) ( 11(яггсиг(" +-/*Ъ"=() ", с) ! ( с==э ) ! с==-'ух' (( с==о) тесцгп 1; сеестл О; // Возвражаез значение переменной. г(сия)е ратяег~:Ггпу чаг(сват *з) ( 1Е(!1за1рпа( я))1 зетхот(1); геспгп О.О: гесоеп чатв[соцррех("сохеп) †'й']; Для проверки новой версии можно вновь применить функцию ма1п(), приведенную в предыдушем разделе. Теперь можно вычислять более сложные выражения, например: | А=)0/4 А-В С=А*(à — 21) ~~~ Проверка синтаксических ошибок при рекурсивном нисходящем анализе Прежде чем перейти к шаблонной версии класса развез, дадим краткое описание проверки синтаксических ошибок.
При вычислении выражений синтаксической ошибкой считается ситуация, в которой входное выражение не соответствует строгим правилам, установленным анализатором. Квк правило, это опечатки, сделаннь)е пользователем. Например, следуюшие выражения будут отвергнугы анализатором. | 10ЯЯХ (10-5)"9) /8 Первое выражение солержит два оператора подряд, во втором нарушен баланс скобок, а последнее выражение начинается со знака операции. Нн одно из этих выражений анализатор не лопускает.
Поскольку ошибочные выражения могут привести к неверной работе программы синтаксического анализа, необходимо предусмотреть средства защиты от них. Просматривая код класса развез, вы, возможно, обратили внимание на функцию ветхое(), которая вызывается в определенных ситуациях. В отличие от других методов синтаксического анализа, рекурсивный нисходящий анализ позволяе~ легко распознавать ошибки, поскольку они могут возникать, в основном, лишь в функциях ввозя(), езпд чах() или еча1 еирв[), где проверяется баланс скобок. Единст- Часть У.
Приложения иа языке С++ Создание обобщенного синтаксического анализатора Две предылушие версии программы предназначались лля синтаксического анализа арифметических выражений типа ((осЬ1е. Однако этим все многообразие выражений не исчерпываемся, Кроме того, жестко заданный тип выражений ограничивает возможности синтаксического анализатора. К счастью, синтаксический анализатор можно представить в виде шаблонного класса, позволяющего обрабатывать выражения произвольных типов, как встроенных, так и пользовательских. Рассмотрим обобщенную версию синтаксического анализатора. // Обобщенный синтаксический анализатор.
В1пс1цде <1ояетеяв> Вьпс1цс)е <сяег)11Ь> Взпс1цг)е <ссеуре> $1пс1цг)е <сясхьпд> ия1пй пащеярасе ясг)/ епцщ Сурев ( ПЯЬТИТТДП = 1, Члйтдпт.в, НЦНВВП)/ сопят м в Бцмчдвв = 26/ Сещр1аее <с1аяя РТуре> с[зат ехр рст; свах сакеп[80); с'пет со)с суре; РТуре чака [ЖЖЧАдЯ) / с1аяя ратяек ( // Ссылаетсл на выражение. // Хранит текущую лексему. // Хранит тип лексемы.
// Хранит значения переменных. чоЫ еча1 ехр1(РТуре чоЫ еча1 ехр2(РТуре чоЫ еиа1 ехрз(РТуре чо16 еча1 ехрс(РТуре вкеяц1с)," атеяц1с); атеяц1с): ахеяи1с); Глава 40. Синтаксический анализ выражений венная проблема, связанная с распознаванием синтаксических ошибок заклк:чаетсч в том, что в нашей программе не предусмотрено прекращсцнс программы ьрк их обнаружении. Это может привести к многократному выволу на экран повг:рхк'. шихся сообщений об о~цибках. Лучше изменить функцию яеххот() так, побы она выполняла выход из ф)нкции. Например, все компиляторы языка С++ содержат пару функций, называемых яеезерО и 1опябщр().