Построение синтаксического графа.
Для построения синтаксического анализатора можно использовать два различных метода. Один из них – это написать универсальную программу грамматического разбора, пригодную для всех возможных грамматик заданного класса. В этом случае конкретные грамматики задаются этой программе в виде данных некоторой структуры, которая управляет ее работой. Поэтому такая программа называется таблично-управляемой. Другой метод – это разработка программы грамматического разбора специально для заданного конкретного языка; при этом его синтаксис по определенным правилам отображается в последовательность операторов, т.е. в программу. Такую реализацию разбора будем называть программно- управляемой. Каждый из этих методов имеет свои преимущества и недостатки. При построении транслятора для конкретного языка программирования вряд ли потребуется высокая гибкость и параметризация, свойственные универсальной программе. Программа грамматического разбора, предназначенная специально для данного языка, обычно оказывается более эффективной и с ней легче работать, легче встроить выполнение параллельно с синтаксическим разбором различных дополнительных действий, например, проверки соответствия типов данных и пр., что может оказаться особенно важно при реализации специализированных языков САПР. Использование универсальной программы позволяет разработать синтаксический анализатор максимально быстро, с меньшими требованиями к квалификации разработчика (в идеале, он должен только представить в требуемой форме и ввести грамматику реализуемого языка). В любом случае полезно представлять заданный синтаксис в виде так называемого синтаксического графа (синтаксической диаграммы, графа распознавания). Такой граф отражает управление ходом работы при грамматическом анализе предложения.
Для нисходящего грамматического разбора характерно, что цель анализа известна с самого начала. Эта цель – распознать предложение, т.е. последовательность символов, которая может порождаться из начального символа. Применение порождающего правила, т.е. замена одного символа последовательностью символов, соответствует расщеплению одной цели на некоторое число подцелей, которые должны следовать в определенном порядке. Поэтому нисходящий метод можно называть также целеориентированным грамматическим разбором. При построении программы грамматического разбора можно воспользоваться этим очевидным соответствием между нетерминальными символами и целями: для каждого нетерминального символа строится своя процедура грамматического разбора. Цель каждой такой процедуры – распознавание части предложения, которая может порождаться из соответствующего нетерминального символа. Поскольку мы хотим построить граф, представляющий всю программу грамматического разбора, то каждый нетерминальный символ будет отражаться в подграф.
Правила построения синтаксического графа:
А1. Каждый нетерминальный символ A с соответствующим множеством порождающих правил
A::=β1 | β2 |...| βn
отображается в синтаксический граф A, структура которого определяется правой частью порождающего правила в соответствии с А2-А6.
А2. Каждое появление терминального символа x в βi соответствует оператору распознавания этого символа во входном предложении. На графе
это изображается ребром, помеченным символом x, заключенным в кружок
или овал:
А3. Каждому появлению нетерминального символа B в βi соответствует обращение к процедуре распознавания B. На графе это
изображается ребром, помеченным символом B, заключенным в прямоугольник:
А4. Порождающее правило, имеющее вид A::= β1 | β2 |...| βn
отображается в граф, где каждое получено применением правил А2-А6
к βi .
А5. Строка β, имеющая вид β=α1 α2 ... αm отображается в граф
где каждое получено применением правил А2-А6 к αi .
А6. Строка β, имеющая вид β={α}* отображается в граф
где получено применением правил А2-А6 к α .