Скачиваний:
38
Добавлен:
01.05.2014
Размер:
765.95 Кб
Скачать

23.2. Синтаксический анализатор

Синтаксический анализатор представлен в виде DC-грамматики(рассмотрениюDC-грамматикпосвящена гл. 16).Предикат parse,как показано в программе 23.1, обеспечивает взаимосвязь с представлением синтаксического анализатора в видеDC-грамматики, в котором на верхнем уровне используется предикат pl_program. Как было отмечено в гл. 16, DC-грамматикаимеет единственный аргумент-структуру, соответствующую предложениям языка. Для трансляции DC-грамматикив предложения Пролога предполагается использовать вариант программы 16.2. Одно из требований, связанное с использованием этой программы, состоит в том, чтобы последний аргумент предикатов, определяемых DC-грамматикой,был разностным списком:

parse(Source, Structure)  pl_program (Structure,Source\ [ ]).

Первым оператором в любой PL-программедолжен быть оператор program. Оператор programсостоит из слова program,за которым следует имя программы. Слова, по которым определяется, какое правило грамматики должно быть применено, будем называтьстандартными идентификаторами.Слово program- пример стандартного идентификатора. Имя программы в языке является некоторым идентификатором. Что представляют собой идентификаторы и константы, будет обсуждаться при рассмотрении арифметических выражений. После имени программы ставится точка с запятой (этот знак также считается стандартным идентификатором). а затем начинается собственно текст программы. Тело PL-программысостоит из операторов или, более точно, из единственного оператора, который сам может состоять из нескольких операторов. Все это выражается следующим грамматическим правилом верхнего уровня:

pl_program(S)  [program], identifier(X), [‘;’], statement(S).

Структура, полученная в результате разбора, является оператором, составляющим тело программы. Для целей генерации кода оператор верхнего уровня programне имеет смысла и не включается в создаваемую структуру.

Сначала рассмотрим синтаксис составного оператора.Этот оператор начинается стандартным идентификатором begin,за которым следует первый оператор S составного оператора, а затем -остальные операторы Ss.Структура, получаемая после разбора составного оператора, имеет вид (S; Ss),где точка с запятой используется как двуместный инфиксный функтор. Она рекурсивна, так как ее элементы Sи Ssмогут быть составными операторами или содержать составные операторы. Точка с запятой выбрана в качестве функтора по аналогии с ее использованием в PLдля обозначения последовательности операторов. Грамматическое правило для составного оператора имеет следующий вид:

Statements(( Ss))  [begin], statement(S),rest_statements(Ss).

Операторы в языке PLразделяются точками с запятой. Соответственно конструкция rest_statements(остальные операторы) определяется как точка с запятой ,за которой следуют непустой оператор и (рекурсивно) «остальные операторы»:

rest_statements((S; Ss))  [‘;’], statement (S),rest_statements(Ss).

Конец последовательности операторов обозначается стандартным идентификатором end. Атом void(пусто) используется для отметки конца оператора во внутренней структуре. Таким образом, исходное определение для rest_statements:

rest_tatements( void)  [end].

Приведенные выше определения операторов исключают возможность использования в языке пустого оператора: программы и составные операторы в языке PLне могут быть пустыми.

Следующим рассмотрим оператор присваивания. Он имеет простое синтаксическое определение: за левой частью оператора идет стандартный идентификатор :=,за которым следует правая часть оператора. Левая часть в языке PLможет быть только идентификатором, в то время как правая является произвольным арифме­тическим выражением. Оператор присваивания имеет следующее определение:

statement(assign(X,E))  identifier(X),[‘:=’].expression(E).

Структура после успешного .распознавания оператора присваивания имеет вид assign(X,E).ПеременнаяЕпредставляет в Прологе структуру арифметического выражения, в то время как Х является именем переменной (в программе на PL). которой должно быть присвоено значение арифметического выражения. Неявно предполагается, чтоХбудет идентификатором в PL.

Для простоты ограничимся некоторым подклассом арифметических выражений, определяемым двумя правилами. Выражение есть или константа, или константа, за которой следуют арифметический оператор и рекурсивно арифметическое выражение. Примерами выражений в этом подклассе являются х, 3, 2tиx+y-z/2,а также выражение в тестовом предложении test1в программе 23.2.Синтаксис выражений этого подкласса определяется следующими грамматическими правилами:

expression(X)  pl_constant(X).

expression(expr(Op,X,Y)) 

pl_constant(X), arithmetic_op(Op), expression (Y).

В выражениях рассматриваемого подкласса используется нестандартный приоритет выполнения арифметических операторов. Например, выражение х*2+yбудет разобрано как выражениех(2+у). Сдругой стороны, выражениех+у-z/2 однозначно интерпретируется какх+-(z/2)).Ограничения, введенные выше, сделаны с целью упрощения программы и изложения материала данной главы.

Ограничимся также и двумя типами констант языка PL:идентификаторами и целыми числами. Спецификация констант задается двумя правилами pl_constant. Каждому правилу соответствует определенная выходная структура. ИдентификаторамХсоответствует структурапате(Х),а целым числам .Y-структура number(X).Необходимые правила записываются следующим образом:

pl_.conslant(name(X))  identifier(X).

pl_constant(number(X)) pl_integer(X).

Для простоты предположим, что целые и идентификаторы в языке PLявляются целыми и атомами языка Пролог соответственно. Это позволяет использовать системные предикаты Пролога для распознавания идентификаторов и целых чисел вPL.Напомним, что фигурные скобки в DC-грамматикахиспользуются для спецификации целей в Прологе:

identifier(X) [X], {atom(X)}. plJnteger(X)  [X], {integer(X)}.

В действительности, чтобы повысить эффективность программы, во всех правилах грамматики, в которых используются идентификаторы и константы языка PL, необходимо непосредственно вызывать соответствующие предикаты языка Пролог.

Для завершения определения арифметических выражений необходим список арифметических операторов. Предложение для определения арифметического оператора сложения, представляемого знаком «+»,имеет следующий вид:

arithmetic_op(‘+’) [‘+’].

Грамматические правила для операторов вычитания, умножения и деления аналогичны; они содержатся в программе 23,2-полной программе синтаксического анализа.

Теперь рассмотрим условный оператор, или оператор if-then-else.В языке PL этот оператор имеет обычный синтаксис: за стандартным идентификатором if следует проверка условия, которая будет определена ниже. После проверки записывается стандартный идентификатор then,за которым следуют некоторый оператор(then-часть), стандартный идентификатор elseи, наконец, оператор, являющийсяelse-частью условного оператора. Синтаксический разбор условного оператора завершается построением структуры if(T,S1,S2).где Т условие, S1-then-часп. S2-else-часть,Синтаксис условного оператора определяется следующим правилом:

statement(if(T,Sl,S2)) 

[if], test(T), [then], statement(Sl), [else], statement(S2),

Проверка условия определяется выражением, за которым следуют оператор сравнения и другое выражение. Структура, формируемая после успешного разбора проверки, имеет вид compare(Oр, Х, Y),гдеOр-оператор сравнения, аХиY - левое и правое выражения в проверке соответственно. Проверка определяется следующими правилами:

test(compare(Op,X,Y)) 

expression (X),comparison_op(Op), expression (Y).

Определение операторов сравнения с использованием предиката comparisons_op аналогично определению арифметических операторов с помощью предикатаarithmetic_ор.Программа 23.1содержит определения для операторов сравнения: =,  >, <, , .

Оператор whileсостоит из проверки условия и действия, которое выполняется, если проверяемое условие истинно. Успешный разбор оператора whileзавершается построением структуры while (Т, S),где Т-проверка (условие), а S-действие(оператор). Синтаксис оператора whileопределяется следующим правилом:

statement (while(Т, S)) [while]. test(T), [do], statement (S).

Для ввода-вывода в языке PLпредусмотрены простой оператор ввода (операторread)и простой оператор вывода (оператор write).Оператор ввода состоит из стандартного идентификатора read,за которым следует идентификатор языка PL. Разбор оператора ввода завершается построением структуры вида read(X),в которой Х идентификатор. Оператор вывода определяется аналогично. Синтаксис операторов ввода-вывода определяется следующими правилами:

statement (read (X))  [read], identifier(X). statement(write(X))  [write], expression (X).

Рассмотренные выше фрагменты DC-грамматикивкупе дают синтаксический анализатор для рассматриваемого языка. Отметим, что, опуская аргументы в полученной DC-грамматике,можно получить формальную БНФ-грамматику для языка PL.

Рассмотрим, как ведет себя синтаксический анализатор при обработке тестовых данных, содержащихся в программе 23.2.При разборе двух однооператорных программ формируются структуры, имеющие следующую форму: <structure>; void, где <structure>представляет разобранный оператор. Оператор writeтранслируется в структуру

write (expr ( +.name(x),expr ( - .name(у) ,expr (/,name(z),number (2))))),

а оператор if then-else в структуру

Программа test1:

write(expr(+,name(x),expr(-,name(y),

expr (/, name (z), number (2))))); void

Программа test2:

if(compare( > ,name(a),name(b)),assign(max,

name (a)), assign (max, name(b))); void

Программа test3:

read(value); assign (count, number(l));assign(result, number(l));

write(compare(<,name(count),name(value)),

(assign (count, expr(+,name(count.).number(l)));

assign (result, expr(*, name(result),name(count))); void));

wrile(name(result));void

Рис. 23.5. Результаты разбора для тестовых программ.

if(сompare(>,name(a), name(b)),assigne(max, name(a)),assign(max, name(b))).

Программа factorialпосле разбора представляется последовательностью из пяти операторов, за которыми следует void.Результаты разбора всех трех тестовых программ представлены на рис. 23.5.Они являются входом для второй фазы компиляции-генерации кода.

Соседние файлы в папке prolog14_end