Анимация
JavaScript
|
Главная Библионтека Существует несколько способов решения задачи игры в домино. Можно начать с верхней части доски с начального символа и пытаться пройти вниз к предложению или же начать с предложения и попытаться пройти наверх к начальному символу. Допускается начать с левого края доски и идти к правому или наоборот, либо от верхнего левого угла идти к нижнему правому углу доски. Вполне естественно желание решать задачу, не передвигая однажды поставленную пластинку, или же отказаться от какой-либо части решения и начать сначала. Большинство этих методов решения задачи соответствует конкретным методам разбора. Методы разбора обычно бывают нисходящими, т.е. начинают с начального символа и идут к предложению, или восходящими, т.е. начинают с предложения и идут к начальному символу. Разрешается смешивать эти два метода. Предложение, разбираемое слева направо, читается нормально, хотя с точки зрения разбора читать его справа налево может оказаться так же просто (или еще проще). Принцип никогда не поднимать уже положенную пластинку в игре такого типа соответствует принципу никогда не отказываться от решения применять конкретное порождающее правило в процессе разбора. Отказ от решения в разборе иногда называют возвратом. Методы разбора могут быть недетерминированными или детерминированными в зависимости от того, возможен возврат или нет. Недетерминированные методы разбора весьма дорогие с точки зрения памяти и времени и крайне затрудняют включение в синтаксический анализатор действий, выполняемых во время компиляции, результаты которых позднее должны быть аннулированы (например, построение таблицы символов и т.п.). В настоящей книге мы имеем дело почти исключительно с детерминированными методами разбора. В гл. 4 описывается метод LL, который осуществляет разбор сверху вниз и использует левосторонние выводы, а в гл. 5 - метод LR, который осуществляет разбор снизу вверх и использует правосторонние выводы. Мы в основном рассматриваем контекстно-свободный разбор, но эти методы можно распространить и на определенные классы универсальных грамматик, таких, как атрибутивные [57] и аффиксные [40]. W-грамматики менее удобны для разбора из-за своего более общего характера. Упражнения 2.1. Приведите грамматику для следующих языков: 2.2. Является ли какой-либо из этих языков регулярным? Обоснуйте свой вывод. 2.3. Найдите регулярную грамматику, генерирующую тот же язык, что и грамматика со следующими порождающими правилами (S - начальный символ): SAB A-XIY Yy\yY В-*Ь\ЬВ 2.4. Выпишите регулярное выражение, генерированное грамматикой из упражнения 2.3. 2.5. Покажите, что грамматика со следующими порождающими правилами является не однозначной: 2.6. В некоторой грамматике S является начальным символом, а порождающие правила следующие: S->real 1DL1ST IDLIST->IDLIST, ID 1DLIST->ID ID->a ID->b ID->c Составьте дерево разбора для предложения real a, b, с 2.7.Выведите грамматику, генерирующую все строки (включая пустую) по алфавиту {0, 1}. 2.8.Рассмотрите следующую грамматику с начальным символом PROGRAM: РRОGRAM- begin DECS; STATS end DECS->d; DECS d STATS->s; STATS s Дайте левосторонний и правосторонний выводы begin s; s; d; d end 2.9. Идентификатор в Фортране состоит из последовательности, включающей до шести букв и цифр, первая литера из которых должна быть буквой. Выведите: а) регулярное выражение для идентификатора; б) грамматику типа 3, генерирующую все без исключения идентификаторы Фортрана. 2.10. В большинстве языков имеется условное предложение или оператор, аналогичный описанному в упражнении 2.5. Объясните, как в некоторых знакомых вам языках избегают потенциальной неоднозначности или как там разрешается этот вопрос. Глава 3 ЛЕКСИЧЕСКИЙ АНАЛИЗ Первой фазой процесса компиляции по идее является лексический анализ, т. е. группирование строк литер, обозначающих идентификаторы, константы или слова языка и т. д., в единые символы. Конечно, в однопроходных компиляторах этот процесс идет параллельно с другими фазами компиляции. Однако независимо от того, составляет ли лексический анализ отдельную фазу компиляции, при описании конструкции компилятора, а также его построении удобно представлять себе лексический анализ как самостоятельную фазу. В настоящей главе мы рассмотрим, как распознаются различные символы, и опишем таблицы, которые лексический анализатор должен построить для себя и для последующих фаз компилятора. Обсудим также конкретные проблемы, встречающиеся при написании лексических анализаторов для Алгола 68 и других языков. 3.1. РАСПОЗНАВАНИЕ СИМВОЛОВ Характер распознаваемых строк может намного упростить процесс лексического анализа. К примеру, возьмем такие идентификаторы, как first datel no1234. вещественные числа 1.23 6.0 34.291(-20, слова языка begin for case Все эти строки можно генерировать с помощью регулярных выражений. Например, вещественные числа можно генерировать посредством регулярного выражения (+-) digit*.digit digit* (l0(+-D digit digit*D 0 1 2 3 4 5 6 7 8 9 10 [ 11 ] 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |