
- •Теория языков программирования и методы трансляции
- •1. Языки и кризис программирования.
- •2. Математические методы описания языков программирования.
- •3. Основные понятия и определения.
- •1. S→ab→aAb→aaAb→aaaB→aaabB→aaabbB→aaabbbB→aaabbbb.
- •2. S→ab→AbB→AbbB→AbbbB→Abbbb→aAbbbb→aaAbbbb→aaabbbb
- •Load a mult b
- •4. Этапы построения транслятора.
- •5. Основные методы поиска ошибок в исходных текстах программ.
- •Устройство управления
- •6. Современное состояние и перспективы развития программирования трансляторов.
- •7. Лексический анализ. Интуитивный подход.
- •8. Классы лексем и их особенности.
- •9. Формирование таблиц лексем и построение дескрипторного текста исходной программы.
- •На основании составленных таблиц можно записать входной текст через введённые дескрипторы (дескрипторный текст):
- •10. Синтаксический анализ. Метод рекурсивного спуска.
- •11. Пример грамматики упрощенного языка Паскаль.
- •12. Пример программы на упрощенном Паскале.
- •Var sum, sumsq, I, value, mean, variance : integer;
- •13. Алгоритм синтаксического анализа по методу рекурсивного спуска.
- •Void read()
- •Void idlist()
- •Void assign()
- •15. Лексический анализ, регулярные грамматики и конечные автоматы.
- •17. Грамматики типа ll(k). Алгоритмы построения магазинных анализаторов.
- •18. Детерминированный синтаксический анализ сверху вниз.
- •19. Правила определения детерминированного мп-разпознавателя по ll(1) грамматике.
- •20. Заключение.
- •Литература
2. Математические методы описания языков программирования.
Формальные языки и грамматики.
Систематическое исследование математических методов для описания языков восходит к 1960 году. Тогда обнаружилось, что формы Бекуса, которые использовались для описания синтаксиса языка АЛГОЛ-60, имеют строгое формальное обоснование с помощью средств математической лингвистики. С этого времени и началась история развития и применения формального математического аппарата – теории формальных языков и грамматик, для проектирования и конструирования трансляторов.
Грамматика – это система правил, позволяющая строить конечные последовательности символов и приписывать каждой из них некоторую структурную характеристику. Формальная грамматика – частный случай исчисления, одно из средств эффективного задания множеств. Система правил, позволяющих строго описывать грамматические закономерности языков. Исчисление – это формальный аппарат оперирования с символами и сочетаниями символов определённого вида. Точное построение того или иного исчисления осуществляется указанием алфавита символов, множества исходных слов (аксиом) и правил вывода, указывающих как получить новые слова из аксиом или уже выведенных слов. Множество слов, выводимых в том или ином исчислении, является рекурсивно перечисляемым.
Разновидности формальной грамматики – грамматика, порождающая и грамматика распознающая. Это деление условно. Грамматика распознающая – система правил, позволяющая определить является ли данная строка предложением определённого языка. Любая распознающая грамматика по существу задаёт способ построения всех строк языка и в этом смысле является порождающей.
Строка – конечная последовательность символов ai , каждый из которых принадлежит некоторому конечному алфавиту Σ. Символы могут повторяться. Пробел может входить в алфавит. Тогда строки могут содержать пробелы. Обозначим пробел через ε.
Например, пусть Σ = {0,1}, тогда 0111000101010111010101 – строка длиной 22. Строка, не содержащая ни одного символа – пустая строка, длиной 0.
Если строка х длиной m, а строка у, длиной n, то объединение строк
xy
строка длиной m+n.
Объединение ассоциативная операция
(xy)z = x(yz),
но не является коммутативной:
xy ≠ yx.
Для пустой строки:
εx = xε=x.
Если строка z=xy, то x – префикс строки z, а у – суффикс строки z.
Если z = xwy, то w – подстрока строки z.
3. Основные понятия и определения.
Формальным языком Ɫ над алфавитом Σ называется произвольное подмножество множества строк построенных над этим алфавитом Σ.
Если Ɫ1 и Ɫ2 – два формальных языка, то их объединение
Ɫ1
Ɫ2
= {xy
│x
Ɫ1
,
y
Ɫ2
}
также является формальным языком.
Для произвольного формального языка Ɫ справедливо равенство
Ɫ{ε} = {ε}Ɫ =Ɫ.
Но язык, состоящий из пустой строки {ε} не является пустым множеством.
Объединение формального языка с самим собой записывается как Ɫ2 . Или в общем случае
Ɫ0 ={ε};
Ɫ1 = Ɫ;
Ɫi = ⱢⱢi-1 = Ɫi-1Ɫ, для i≥2.
Замыкание Клини формального языка Ɫ обозначим Ɫ* и оно может быть определено как:
Ɫ*
=
Ɫi.
И по аналогии:
Ɫ+
=
Ɫi.
Большинство современных работ, посвящённых языкам программирования высокого уровня, обязательно содержат раздел, где формально определяется синтаксис языка. Например, синтаксис Паскаля обычно описывается набором синтаксических диаграмм.
Беззнаковая
константа я
я
константа
Переменная Идентификатор
функции
(
)
Выражение
,
, Выражение
(
)
˥ Множитель
Рис.3.1 Синтаксические диаграммы Вирта.
Пусть мы определяем понятие «множитель». Любой маршрут по синтаксической диаграмме обязательно пройдёт через несколько её узлов: один, два, три, четыре. Можно показать, что число различных путей по рассматриваемой синтаксической диаграмме равно бесконечности. Каждый из путей соответствует одному из возможных определений понятия «множитель».
Каждому узлу синтаксической диаграммы, изображённому прямоугольником «множитель», соответствует своя диаграмма, определяющая понятие внутри прямоугольника. Подобный узел называют нетерминальным узлом, а понятие ему соответствующее – нетерминальным символом языка. Если узел диаграммы изображён в виде круга, то его называют терминальным узлом, а понятие внутри круга – терминальным символом языка. Это, так называемые, диаграммы Вирта – автора языка Паскаль.
Существует другой путь определения синтаксиса языка, альтернативный синтаксическим диаграммам, - это формы Бекуса-Наура (БНФ). При использовании БНФ нетерминальные символы языка заключаются в угловые скобки ‹ … ›.
Запись вида ::= читается как «есть по определению».
Запись вида := читается как «станет равно».
Символ │ означает «или».
Отсюда, используя БНФ, опишем понятие «множитель»:
‹множитель› ::= ‹без знаковая постоянная›
│ ‹переменная›
│ ‹идентификатор функции›
│‹идентификатор функции›(‹список выражений›)
│(‹выражение›)
│˥‹множитель›
‹список выражений› ::= новый нетерминальный символ, который определяется как
‹список выражений› ::= ‹выражение›│‹выражение›,
‹список выражений›
Нотация БНФ эквивалентна синтаксическим диаграммам Вирта. Любая синтаксически корректная программа, написанная на некотором языке программирования, может быть определена в нотации БНФ как нетерминальный символ некоторого языка. Однако синтаксические определения не составляют полного описания языка программирования. Так, например, кроме текста программы, описать все типы переменных с помощью БНФ нельзя.
Контекстная грамматика. Определим формальную модель грамматики. При этом будем придерживаться следующих соглашений:
- прописные буквы – нетерминальные символы;
- строчные буквы – терминальные символы языка;
- символ → соответствует ::= (станет равно).
Простой пример. Грамматики может иметь следующий набор правил замены нетерминальных символов на терминальные, т.е. набор продукций:
S→A|B
A→aA|a
B→bB|b
Некоторая строка β может быть получена из строки α с помощью одного или нескольких правил вывода – заданных продукций, то будем это записывать как α→β. В нашем случае:
S→A→aA→aaA→aaaA→aaaaA→…→aaaa…a=an
или:
S→an
Проиллюстрировать эту последовательность выводов можно с помощью графа-дерева.
S
A
A
a
A
a
a
Рис. 3.2 Дерево вывода сентенции «ааа».
На рис.1. S – корень дерева, аксиома языка, «а» − листья дерева, терминальные символы языка.
Формализуем введённые понятия. Обобщим правила вывода на строки терминальных и нетерминальных символов языка.
Контекстная грамматика может быть представлена совокупностью четырёх объектов
G ={N,T,P,S}
где:
N – конечное множество нетерминальных символов,
T – конечное множество терминальных символов,
P – конечное множество продукций грамматики,
S – аксиома языка.
Продукции Р – это правила замены нетерминальных символов на строки или отдельные символы. Если в левой части каждой продукции грамматики только один нетерминальный символ, то такая грамматика будет контекстно-свободной (КС), поскольку замена нетерминального символа не будет зависеть от того какие символы находятся рядом с ним. Мы остановимся на КС грамматиках поскольку именно они описывают алгоритмические языки программирования.
Строки символов, содержащие нетерминальные символы называются сентенциальными выражениями. Строки символов из одних терминальных символов называются сентенциями. С помощью продукций грамматики сентенциальные формы преобразуются в сентенции и на этом возможные преобразования заканчиваются.
Множество терминальных строк, выводимое из аксиомы грамматики S
как раз и будет языком, порождённым данной грамматикой G и обозначают
Ɫ(G):
Ɫ(G)
={ xT*│S
→ x
} ,
где * означает, что множество терминальных символов {T} может содержать пробел ε.
Если две грамматики порождают один и тот же язык, то эти грамматики будут эквивалентными.
Например, грамматика
S→aA|bB|a|b
A→aA|a
B→bB|b
будет эквивалентна грамматики с аксиомой
S→A|B
теми же продукциями:
A→aA|a
B→bB|b
так как порождают один и тот же язык, состоящий из слов аn и вm .
Грамматики могут однозначными и неоднозначными. Для однозначных грамматик цепочки вывода заданной сентенции могут быть различны, но дерево вывода для всех случаев одинаково. Для неоднозначных грамматик для разных цепочек вывода заданной сентенции деревья вывода будут различны.
Например. Грамматика
S→A|B
A→aA|a
B→bB|b
будет однозначной, так как дерево вывода не изменяется для всех слов, состоящих из «а» или «в». Но если изменить аксиому в данной грамматике:
S→AB ,
цепочки вывода 1 и 2 дадут разные деревья вывода для сентенции «aaabbbb»