Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

GrandM-Patterns_in_Java

.pdf
Скачиваний:
97
Добавлен:
14.03.2016
Размер:
8.88 Mб
Скачать

,38 Глава 8. Поведенческие шаблоны проектирования

[IOTиния надУ строкой показывает, какая часть строки была идентифицирована.

что нас распозналось:

simpleCoтblnation

I

"ord

I

fox

Iными словами, распозналась лексема simpl eCombina tion, которая состоит

з лексемы слова, содержащей слово fox. Нам нужно распознать именно

ombina tion. Четыре продукции для combination начинаются с simpleCom­

,ina tion. Одна из этих продукций -

combina tion simpleCombina tion

'еперь у нас есть возможность выбора: сравнивать эту ПРОДУКlIИИ с тем, что

же распозналось, или попытаться подобрать более длинную продукцию для 'ombina иоn. В таком случае всегда лучше подобрать более длинную продук­ ,ию. Если нельзя подобрать более длинную продукцию, то возвращаемся подбираем более короткую.

ледующая лексема в строке - and. Существует только одна продукция для 'ombina иоn, начинающаяся с simpl eCombina иоn, за которой следует and.

combination simpl eCombina tion and combina tion

[тобы завершить сопоставление строки с этой продукцией, нам необходимо

аспознать остальную часть строки как combination.

:ледующая лексема в строке - not. Взглянув на продукции для combina tion, :ожно увидеть, что combina tion может начинаться либо с открывающей

кобки, либо с simpleCombination. Поскольку строка, которую мы пытаемсЯ

аспознать как cOmbina tion, не начинается с открывающей скобки, пробуем

аспознать начало строки как simpleCombina tion. Для simpleCombina tion

сть продукция, которая начинается с лексемы not. Теперь попытаемся завер­ IИть подбор продукции

simpleCombination not word

заканчиваем сопоставление

combina tion simpleCombination and combina t i on

[оскольку мы идентиФицировали половину продукции simpl eCombina

редполагаем, что следующей лексемой в строке должна быть лексема

, нас распозналась следующая часть строки:

ti оп, word.

fox and not brown

Little Language - 339

Следуюшая лексема - word. Значит, мы успешно подобрали продукции, кото­ рые пытались сопоставить. Кроме того, на этом содержимое строки исчерпано,

то есть мы распознали всю строку как комбинацию,

имеюшую следуюшую

внутреннюю структуру:

соmbiлаtiоn

 

 

 

 

 

Si"P1eC0

 

 

 

 

j"ination

andI

SimpleC0j"ination

word

andI

not word

foxI

 

not brownI

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

Набор продукций, применявшийся в преДbIДуШем примере, не учитывает все особенности языка словосочетаний. Он не позволяет включать в комбинацию строки в кавычках. Кроме того, он не определяет правила предшествования для and, near или or.

Рассмотренный набор продукций использует одну и ту же нетерминальную лексему для сопоставления последовательностей лексем word и not word. Это

значит, что после построения дерева синтаксического разбора объект одного

и того же типа будет представлять оба вида этих последовательностей. Если

можно будет определить, какой тип последовательности представляет объект,

взглянув только на тип этого объекта, то дерево синтаксического разбора будет

интерпретироваться проше и быстрее. Тогда продукции будут распознавать та­

КИе последовательности как две разные нетерминальные последовательности.

Опишем полный набор продукций, учитываюший изложенные выше уточнения:

сотЬiла t l 0Л оrСотЬiлаtiол

оrСотЬiлаtiол алdСотЬiла tiол or оrСотЬiлаti ол оrСоmЫла ti оn алdСотЬi лаtiол

алdСотЬiла ti ол леаrСотЬinа tiоn and алdСотЬiлаtiоn

andCoтbination леаrСотЬinа tiол

 

nеаrСотЬiла tiоn simрlеСотЬiлаtiоn

near nearCombina tion

леаrСотЬinаtiоn simрl еСотЬiлаtiол

 

simрlеСотЬiлаti оn ( orCombina tion

)

интервал содержит один или более последовательно расположенных знаков пробелов, табуляций, перехода на новую строку или конца строки. Интер­ вал может использоваться для разделения терминальных лексем. Он не

Little Language - 341

СКИЙ анализатор должен создавать узлы дерева синтаксического разбора, со­ держащие информацию.

Мы рассмотрели основы написания продукций для распознавания лексем. Те­ перь опишем способы задания правил лексического анализа, определяюших,

как распознавать терминальные лексемы в последовательности символов.

Во многих малых языках лексические правила бывают настолько простыми, что можно адекватно определять их посредством набора описаний, выполнен­ ныхна естественном языке, например:

имеет друтого значения и отбрасывается; а,

лексема and состоит из следующей последовательности трех букв: n, d;

Можно, конечно, отдать предпочтение более точному методу, основанному на регулярных выражениях. Регулярные выражения представляют собой способ, КОТОРЫЙ определяет порядок совпадения последовательности символов. На­ пример, регулярное выражение

[0-9] +

соответствует последовательности, состоящей из одной или более цифр. Мож­ но использовать разнообразные системы обозначений регулярных выражений,

но они должны быть достаточно выразительными для большинства малых язы­

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

Таблица 8.1. Язык словосочетаний

Регулярное

с чем оно совпадает

выраженне

с

Символом с, если с не является одним из специальных символов, опи­

 

санных ниже

\Если за \ следует одна из управляющих последовательностей , которую

язык Java разрешает использовать в строках, то это означает то же са­

, что и данная ая последовательность. Если з \ указаны

мое то управляющ а

какие-либо символы , которые считаются специальными в регулярных

выражениях, в паре символов второй символ не считается специаль­ ным. Например, \\ совпадает с символом обратной наклонной черты

Любым символом

$Концом последовательности или строкиНачалом последовательности или строки

342

Глава 8. Поведенческие шаблоны проектирования

 

 

Таблица 8.1. Окончание

с

 

 

 

Регулярное

 

чем оно совпадает

 

 

выраженне

 

 

 

 

 

 

 

 

 

 

 

[s]

 

Символом, находящимся в наборе и диапазонах символов. Например,

 

[ aeiouy] совпадает с гласными нижнего регистра.

[A-Za -z_ ] совпада­

 

 

 

ет со всеми буквами Bepxнero или нижнего регистра или с подчеркива­

[ "s]

 

нием

 

 

 

 

 

Символом, не находящимся в наборе символов и в диапазонах симво­

 

лов. Например, {"О-9] совпадает с символом , который не является

 

 

 

цифрой. Такая трактовка символа " соответствует случаю, когда символ

 

 

 

стоит справа от [.

Например, [+"] совпадает со знаком плюс или цир­

г*

 

кумфлексом

 

 

 

 

 

Нулем или более случаями появлениями регулярного выражения r

г+

 

Одним или более случаями появления регулярного выражения r

 

Нулем или одним случаем появления регулярного выражения r

rx

 

Совпадает, если совпадает регулярное выражение r и следующее за ним

(г)

 

регулярное выражение х

 

 

 

 

Совпадает, когда совпадает регулярное выражение

Например, (xyz) *

 

ности .xyz

 

 

 

последователь­

 

совпадает с нулевым или более количеством появленийг.

 

 

 

Любой строкой, которая содержит регулярное выражение r или регуляр­

 

 

 

ное выр жение х.

Например, ( аЬс) I ( yz) совпадает с последователь­

г{т,n}

 

ностью аЬс или последовательностью xyz

 

 

 

По крайней мере, с т, но не более чем с n количеством появлений регу­

в табл.

 

лярного выражения r

 

 

 

8.2 представлен набор правил лексического анализа для языка словосо­

четаний. В первом столбце находится регулярное выражение. Второй столбец содержит имя терминальной лексемы или любое выражение, которое считаетсЯ

опознанным, если оно совпадает с регулярным выражением. Если синтаксиче­

ский анализатор должен обнаружить у себя на входе следующую терминальную

лексему, он будет проверять регулярные выражения в порядке их появления до

тех пор, пока не обнаружит одно, совпадающее с входными данными. Если НИ

одно регулярное выражение не совпало с входными данными, то синтаксиче­ ский анализатор знает, что входные данные неверны.

Если регулярное выражение совпадает с некоторыми входными данными и су­

ществует терминальная лексема, заданная во втором столбце, то синтаксиче­

ский анализатор распознает эту лексему и обрабатывает ее в соответствИИ с продукцией, которую он пытается подобрать. Если во втором столбце нет тер­ минальной лексемы, то входные данные, совпадающие с регулярным выраже­ нием, отбрасываются, и синтаксический анализатор опять начинает с первого регулярного выражения.

Little Language 8 343

Таблица 8.2. правила лексического анализа для языка словосочетаний

 

 

Реryлярное выражение

Имя терминальиой лексемы

 

 

или опознанное выражение

 

 

 

( \и О О О О - \ иОО2 0 ] +

пробел. Т.е. игнорируется

[00] ( Rr ]

 

or

(Аа]

(Nn ]

( Dd]

and

(Nn ]

( Ее ]

( Аа ] ( Rr ]

near

(Nn] (00] ( T t ]

not

( a-zА- Z О- 9 ] +

word

\ (

 

 

 

\ )

 

 

 

\\

( ( " " ] *

( \ \ " )

* )

* "

Мы определили синтаксис языка словосочетаний и рассмотрели семантику языка. Теперь нам нужно спроектировать классы. На рис. 8.8 представлены классы, необходимые для реализации языка словосочетаний.

Экземпляры класса LexicalAnal yzer читают символы комбинации слов

из экземпляра класса InputStream. Экземпляр класса Parser читает лексемы

из экземпляра класса LexicalAnalyzer, вызывая его метод nextToken. Как

показано на диаграмме, метод nextToken возвращает объект TerminalToken.

Однако на диаграмме нет класса TerminalToken. Если бы он был, то являлся

бы абстрактным классом, не определяющим ни методов, ни переменных. Каж­ дый из подклассов класса TerminalToken соответствовал бы отдельной терми­

нальной лексеме. Подклассы класса Termina lToken не должны были бы зада­

вать ни методов, ни переменных (или только одну переменную, содержащую

строку, которая была бы опознана как эта терминальная лексема). Такие объ­

екты были бы упрощенными, инкапсулирующими терминальную лексему

Только одного типа, и в некоторых случаях - строку. Реализации шаблона

Li ttle Language обычно не берут на себя труд инкапсулировать такого рода ИнФормацию. Реализации класса LexicalAnal yzer обычно предоставляют эту часть информации в виде неинкапсулированных отфрагментов информации.

По мере того как объект Parser получает лексемы объекта LexicalAnalyzer,

ОН создает экземпляры подклассов класса Combina t i on, выстраивая их в виде дерева синтаксического разбора.

Класс Combina t ion представляет собой абстрактный суперкласс для всех клас­

сов, которые инстанциируются для создания узлов дерева синтаксического раз­

бора. Класс Combi nation определяет абстрактный метод contains, который

Принимает в качестве аргумента строку и возвращает массив чисел типа int.

344 Глава 8. Поведенческие шаблоны проектирования

Подклассы класса Combination замешают метод contains с целью определе­

ния, удовлетворяются ли требования некоторого подкласса, предписываЮШИе

содержание нужной комбинации слов. Если строка содержит нужную комби­

нацию слов, метод contains возврашает массив целочисленных значений, ко­ торые представляют собой смешения слов в этой строке. Если строка не содер­

жит нужной комбинации слов, то метод conta ins возврашает nul l .

 

 

 

 

 

 

 

 

 

 

 

 

СоmЫпаОоп

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

contains(s:Stгing) :int[}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

t

 

 

 

 

 

NearComblnation

 

OrComblnation

 

 

 

 

 

 

 

 

AndComblnation

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

teftChild : Comblnation

 

leftChild : Comblnation

 

 

 

 

 

 

leftChild : Comblnation

 

rightChild : Comblnation

 

 

 

 

 

rightChild : Comblnation

 

 

 

 

 

rightChild : Comblnation

 

contains(s:String):int[]

 

 

 

 

 

contains(s:String):int[]

 

 

 

 

 

 

contains(s:String):int[]

 

*

 

 

 

 

 

 

 

 

 

*

 

l'

 

 

 

 

 

 

 

 

* 1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

OrComblnation

 

 

 

AndComblnation

 

 

 

 

 

 

 

 

word : String

 

 

 

 

 

 

 

word : String

 

 

 

 

 

 

 

 

 

 

 

Создает'"

 

contains(s:String):int[]

 

 

 

contains(s:String):int[]

 

 

 

"' Создает

 

 

*

....Создает

Создает'"

 

 

 

 

 

*

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Создает'"

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

Parser

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

 

 

 

 

f--

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

parser(input:lnputStream):Comblnation

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-

 

Считывает лексемы из

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

J ,

1

 

 

 

1

 

 

 

Считывает символы из

* .,J InputStream

 

 

 

LexicatAnaLyzer

 

 

 

 

 

 

 

nextТokenO : TerminaLToken

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 8.8.

Классы языка словосочетаний

 

 

 

 

 

моти вы

© Нужно идентифицировать,

создавать или форматировать данные похожих

типов,

ь

множество

различных комбинаций немногочислен

н

Ы

Х

испол зуя

 

 

операций.

© Простое представление комбинаций операций может обеспечить прие ле­ мую производительность.

Little Language - 345

РЕШЕНИЕ

Шаблон Little Language начинается с проекта малого языка, который может зада­ вать комбинации операций, необходимых для решения специальных проблем. Проект малого языка определяет его синтаксис при помоши продукций И лекси­ ческих правил, описание которых приводится в разделе « Контекст>.>. Семантика малого языка обычно задается неформальным образом и описывает назначение конструкций малого языка на английском или другом естественном языке.

Следуюшая стадия после определения малого языка - проектирование клас­ сов, используемых для реализации языка. На рис. 8.9 представлена организа­ ция классов, принимаюших участие в шаблоне Little Language.

 

 

CLient

 

 

11

 

1

 

 

 

 

 

 

1. . *

 

AbstradNontermina/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Выполняет

 

execute()

 

 

 

 

 

I

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

ConcreteNonterminaL1

 

 

 

 

 

 

ConcreteNonterminaL2

 

 

 

 

executeO

 

 

 

 

 

 

 

 

 

 

 

 

 

executeO

 

 

 

 

 

 

 

 

 

 

 

 

 

СОЗАает"

 

СОЗАает"1 *

 

 

 

*

т

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1 1Parser

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

parser(input:lnputStream):AbstractNonterminaL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1 т он использует лексемы

 

 

 

источник

 

 

 

"Читает лексемы

 

 

 

 

лексем

· 1

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

LexicalAnaLyzer

 

 

1

 

"Читает символы

 

 

create(input:inputStream)

 

 

 

 

nextТoken() : TerminaLToken

 

 

он использует символы

 

 

 

* . ..I

СОЗАает"

1

1 InputStream 1

источник символов I *

Рис. 8.9. Классы шаблона Liltle Language

Опишем роли, исполняемые этими классами.

CUent. Экземпляр класса в этой роли запускает программу малого языка, предо­

СТавляет ей все необходимые данные и использует результаты, которые получены

в результате работы программы. Он создает экземпляр класса Parser для про­

Грамм синтаксического разбора, которые он предоставляет при помоши объек­

тов InputStream. Метод parse объекта Parser возвращает экземпляр класса

AbstractNonterminal объекту Client. Этот объект является корневым в де­

Реве синтаксического разбора. Объект Cl ient вызывает метод execute объек­

Та Abs tractNonterminal для запуска программы.

546 Глава 8. Поведенческие шаблоны проектирования

Lexical Analyzer. При вызове метод parse объекта Parser создает объект c,exicalAnal yzer ДЛЯ чтения символов из того объекта I nputS tream, кото­ рый был ему передан. Объект LexicalAnalyzer считывает символы из объекта InputStream, распознает при помощи лексических правил найден ные им тер­ минальные лексемы и возвращает эти лексемы классу Parser посредством ВЫ­ зова метода nextToken объекта LexicalAna l yzer. Метод nextToken возвра­

шает следующую терминальную лексему, обнаруженнуюИ во входных данных.

Parser. Объект C l i ent создает объект класса Parser затем вызывает метод pa rse объекта Parser ДЛЯ выполнения синтаксического разбора входных дан­

ных, полученных от объектов I nputStream, сравнивая лексемы входных дан-

ых с грамматическими продукциями. По мере того как он подбирает продук­

ции, метод parse строит дерево синтаксического разбора и возвращает ссылку

на корень дерева синтаксического разбора объекту C l ient.

AbstractNonterminal. Класс в этой роли является абстрактным суперклассом для всех классов, экземпляры которых могут представлять узлы дерева синтак­ сического разбора.

Объект C l i ent вызывает абстрактный метод execute этого класса для выпол­ нения программы. и

ConcreteNonterminall, ConcreteNonterminal2 Т.д. Экземпляры классов, испол­ няющих эти роли, представляют собой узлы дерева синтаксического разбора.

TerminalToken. Этот абстрактный класс не задает ни методов, ни переменных.

Его подклассы представляют терминальные лексемы, распознаваемые классом

LexicalAnalyzer.

InputStream. Экземпляр класса j ava . i o . I nputS tream может использоваться ДЛЯ чтения потока символов.

РЕАЛИЗАЦИЯ

Некоторые программисты полагают, что при реализации синтаксического ана­

лизатора лучше распределить большую часть его логики по множеству классОВ, соответствующих нетерминальным лексемам или даже продукциям. ОбъясняюТ

это программисты чаще всего тем, что такая структура является почему-то бо­

лее объектно-ориентированной. Однако подобную структуру синтаксическИХ

анализаторов

сложнее поддерживать, чем структуру,

описанную выш

е.

о

 

 

 

Эт

объясняется следующими двумя причинами.

по нескольким классам

1 .

Распределение логики синтаксического разбора

приводит в результате к меньшему сцеплению классов, что затрудняет их

 

понимание. Синтаксический анализатор ДЛЯ малых языков обычно доста­

точно мал, поэтому его можно осмыслить целиком. Разброс логики синтак­

сического разбора по множеству классов значительно затрудняет воспри­

ятие синтаксического анализатора во всей его полноте.

2.Если синтаксический анализатор слишком велик для того, чтобы попрнять-

его целикоМ, то можно автоматически создать анализатор на основе о

Little Language 347

дукций. Все имеющиеся инструменты, известные автору этой книги, создают синтаксический анализатор в виде единственного класса или в виде основ­ ного класса с несколькими вспомогательными классами. Если существуют другие средства, создающие синтаксический анализатор в виде множества классов, то организация таких классов, скорее всего, будет отличаться от любой создаваемой вручную структуры. Это значит, что, если синтаксиче­ ский анализатор организован вручную в виде множества классов, переход

кавтоматически создаваемому синтаксическому анализатору приведет

кразрушению классов, так как они ссылаются на исчезнувший класс соз­ данного вручную синтаксического анализатора. Создание синтаксического анализатора вручную в виде множества классов может затруднить переход

кавтоматически создаваемому синтаксическому анализатору.

Большая часть подклассов класса Terminal Token не содержит ни методов, ни переменных. Подклассы класса Terminal Token, которые должны содержать переменные, обычно имеют только одну переменную, значение которой пред­ ставляет собой подстроку входных данных, которая сравнивается экземпляра­

ми класса. Поскольку подклассы класса Terminal Token содержат очень мало

информации, большинство реализаций шаблона Little Language не утруждают себя использованием класса Terminal To ken или его подклассов. Вместо этого они передают синтаксическому анализатору тип лексемы, опознанной лекси­ ческим анализатором, и соответствующую строку, полученную от лексического анализатора, не инкапсулируя в каком-либо объекте.

Синтаксические анализаторы создают синтаксические деревья снизу вверх.

Корень дерева синтаксического разбора соответствует как минимум исходной

программе со всеми ее потомками. По мере того как синтаксический анализа­ тор выполняет синтаксический разбор своих входных данных, он создает не­

большие синтаксические деревья. Распознавая все более и более длинные кон­

струкции, он объединяет небольшие синтаксические деревья в более крупные, Имеющие общий корень. По окончании синтаксического анализа получается только одно большое синтаксическое дерево.

Многие тонкости оптимизации и проектирования, очень важные для полно­

весных языков, не имеют никакого значения для малых языков. Суть в том, что

Способы, описанные в данном шаблоне, недостаточны для проектирования или

реализации языков, подобных языку Java.

СЛЕДСТВИЯ

Шаблон Little Language позволяет пользователям задавать различные ком­

бинации операций. Почти всегда легче спроектировать и реализовать ма­

лый язык, чем GUI, обеспечиваюший такую же гибкость и выразитель­

ность, какую предоставляет малый язык. Несмотря на это, большинство

пользователей считает, что GUI более прост в использовании.

Язык - это форма пользовательского интерфейса. Подобно работе с любым

другим пользовательским интерфейсом , изучают, что с его помощью дела-

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]