![](/user_photo/2706_HbeT2.jpg)
GrandM-Patterns_in_Java
.pdf![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6341x1.jpg)
348 • Глава 8. Поведенческие шаблоны проектирования
ется просто, а что не очень, и наблюдают за другими людьми , которые его
•применяют.
Обычно класс Parser для малого языка реализуется посредством наПИса
ниHbIяМ закрытых методов, которые в основном соответствуют нетерминаль лексемам. Такую схему легко понять, а грамматические изменения легко
реализуются до тех пор, пока грамматика остается относительно небольшой. Если язык становится слишком громоздким, структура делается неуправ ляемоЙ.
Для более значительных и предоставляющих полный сервис языков имеются
более сложные технологии проектирования и реализации. Существуют инстру менты, которые могут автоматически создавать классы Parser И LexicalAna l i zer на основе продукций и правил лексического анализа. Другие инструмен ты помогают при создании и упрощении деревьев синтаксического разбора.
ПРИМЕНЕНИЕ В JAVA API
Подклассы класса j ava . text . Format используют шаблон Little Language. Конструкторам этих классов (явно или неявно) передается строка, содержашая описание формата на малом языке. Каждый подкласс имеет свой собственный малый язык для таких операций, как замена текста в сообщениях (Mes sageFormat), форматирование даты и времени (DateFormat) и форматирова ние десятичных чисел (Decima 1Format).
Ввиду простой структуры таких малых языков их синтаксические анализаторы создают не синтаксическое дерево, а массив объектов.
ПРИМЕР КОДА
в качестве первого примера приведем код лексического анализатора. Лексиче
ские правила языка словосочетаний достаточно похожи на лексические прави
ла языка Java, поэтому класс j ava . io . StreamTokenizer может сделать болSunь
шую часть работы. Это тот самый класс, который компилятор Java фирмы использует для лексического анализа.
class LexicalAnalyzer { |
|
|
|
|
|
|
|
|
|||
private |
StreamTokenizer input; |
|
|
|
|
|
|||||
private |
int |
lastToken; |
|
|
|
|
|
|
|
|
|
/ / Константы для идентификации |
типа последней лексемы . |
|
|
|
|||||||
static |
final |
int INVALID_CНAR |
= |
-1 ;// Непредвиденный СИМВОЛ . |
|
||||||
static |
final |
int NO TOКEN = |
0 ; // |
Еще нет распознанных |
лексе |
|
. |
||||
static |
final |
int OR = |
1 ; |
3 ; |
|
|
м |
|
|||
static final int |
NEAR |
= |
|
|
|
|
|
|
|||
зtаtiс |
final |
int |
AМD = |
2 ; |
|
|
|
|
|
|
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6342x1.jpg)
350 • Глава 8. Паведенческие шаблоны проектирования
|
|
token = EOFi |
|
||
|
|
breaki |
|
|
|
|
сазе StreamToken!zer . TT_WORD : |
||||
|
|
if (input . sval . equalsIgnoreCase ( "or"» |
|||
|
|
token |
= ORi |
|
|
|
|
else |
if |
(input . sval . equalsIqnoreCase ("and"» |
|
|
|
token |
= AND i |
|
|
|
|
else |
if |
(input . sval . equalsIqnoreCase ("near"» |
|
|
|
token |
= NEARi |
|
|
|
|
else |
if |
(input. sval• |
. equalsIqnoreCase ("not" » |
|
|
token |
= NOT i |
|
|
|
|
else |
|
|
|
|
|
token = WORD i |
|
||
|
с:аае \ 11 / |
; |
|
|
|
|
|
break i |
|
|
|
|
|
token = QUOTED_STRINGi |
|||
|
|
breaki |
|
|
|
|
сазе ' ( ' : |
|
|
||
|
|
token |
= LEFТ_PARE Ni |
|
|
|
|
breaki |
|
|
|
|
ае |
: |
|
|
|
|
са token' ) ' |
= RIGHT_PAREN i |
|||
|
|
break i |
|
|
|
|
default: |
= INVALID_CНARi |
|||
|
|
token |
|||
|
breaki |
|
|
|
|
|
// |
swi tch |
|
|
|
catch |
(IOException е) { |
|
|||
// |
IOException считается КОНЦОМ файла . |
||||
token |
= EOFi |
|
|
||
// |
try |
|
|
|
// nextToken ( )
// class Lexi calAnalyzerreturn tokeni
Хотя класс LexicalAnal yzer использует класс StringToken i zer для выпОЛ
нения значительной части лексического анализа, он предоставляет свои соБСТ
венные коды для указания типа распознанной им лексемы, что позволяет изме нять реализацию класса LexicalAnalyzer, не затрагивая классы, использУЮ
шие класс Lexica lAnal yzer.
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6344x1.jpg)
Little Language - 351
При реализации синтаксического анализатора используется способ, который называется рекурсивным спуском. Синтаксический анализатор, применяющий
рекурсивный спуск, имеет методы, которые соответствуют нетерминальным
лексемам, определяемым грамматическими продукциями. Эти методы вызыва
ют друг друга, используя примерно тот же шаблон, согласно которому ссыла
ются друг на друга соответствующие грамматические продукции. Там, где есть
рекурсия в грамматических продукциях, обычно имеется рекурсия и в методах.
Одно важное исключение из этого правила относится к тому случаю, когда ре курсия является саморекурсией, выполняемой через крайнюю правую лексему
продукции, например:
orCombination -+ andCombination or orCoтbination
Это можно пояснить так: метод саморекурсии создает метод, который выпол няет саморекурсию, и это последнее, что он делает перед возвратом результата.
Рекурсия такого вида представляет собой специальный случай, получивщий
название концевой рекурсии. Концевая рекурсия является специальным видом рекурсии, поскольку ее всегда можно преобразовать в цикл. Следующий код для класса Parser демонстрирует методы, соответствующие нетерминальным лексемам , которые определены при помощи саморекурсии. Эти методы реали зуют саморекурсию, используя цикл.
public class Parser {
private LexicalAnalyzer lexer i / / Лексический анализатор . private int tOkeni
/ * * * Синтаксический анализ комбинации сло в , прочитанных
*из данного входного потока .
*@pa ram input
Чтение комбинаций слов из этого InputStream .
* @ return Объект комбинации, который является корнем
* / |
синтаксического дерева . |
|
public Combination parse (InputStream input) throws SyntaxException{
lexer = new LexicalAnalyzer= (input)i i Combination с orConbination () expect (LexicalAnalyzer . EOF) i return с ;
/ / parse ( InputStream)
private Combination orConbination ( throws SyntaxException {
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6345x1.jpg)
352 • Глава 8. |
|
Поведенческие шаблоны проектироваНИА |
|||
Combination с |
|
= |
andCombination () ; |
||
while |
(token |
== |
LexicalAnalyzer . OR) |
||
с |
= |
new OrCombination (c , andCombination (» ; |
|||
} / / |
whi l e |
|
|
|
|
return с; |
|
|
|
||
/ / orCombinat ion ( ) |
|||||
private Combination andCombination ( ) |
|||||
|
|
throws |
SyntaxException { |
||
Combination с |
= |
nearCombination () ; |
|||
while |
(token == LexicalAnalyzer .AND) |
||||
с |
= |
new AndCombination (c, nearCombination (» ; |
|||
} / / |
whi l e |
|
|
|
|
return с ; |
|
|
|
||
/ / andComЬinat ion |
|||||
private Combination nearCombination () |
|||||
|
|
throws |
SyntaxException { |
||
Combination с |
|
= |
simpleConibination ( ) ; |
||
while |
(token |
= LexicalAnalyzer . N E A R) { |
|||
с |
= |
new NearCombination (c, simpleCombination (» ; |
|||
} / / |
whi l e |
|
|
|
return с;
/ / nea rComЬination ( )
private Combination simpleCombination () throws SyntaxException {
if (token == LexicalAnalyzer . LEFТ_PAREN) nextToken () ; =
Combination с orCombination () ; expect (LexicalAnalyzer . RIGHT_PAREN) return (с ;
/ / if \ \
if (token == LexicalAnalyzer . NOT) return notWordCombination () ;
else
return wordCombination () ;
}/ / s impl eComЬination ( )
private Combination wordCombination () throws! = SyntaxException {
if (token ! =LexicalAnalyzer . WORD
" token LexicalAnalyzer. QUOTED_STRING)
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6346x1.jpg)
354 • Глава 8. Поведенческие шаблоны проектирования
String тзС] = "found " + tokenName (token) + " when expecting " + tokenName (t) ; throw new SyntaxException (msg) ;
/ / i f nextToken () ;
/ / expect ( in t )
private String tokenName (int t) (
String tname ; switch (t) (
сазе LexicalAnalyzer= . OR: tname "OR" ;
break ;
сазе LexicalAnalyzer= . AND: tname "AND" ;
break ;
сазе LexicalAnalyzer= .NEAR: tname "NEAR" ;
break;
сазе LexicalAnalyzer= . NOT : tname "NOT" ;
break;
сазе LexicalAnalyzer . WORD : |
|||||
tname |
= |
"word" ; |
|||
break ; |
|
|
|
|
|
сазе LexicalAnalyzer . LEFТ_PAREN : |
|||||
tname |
= |
" |
( |
" |
; |
break; |
|
|
|||
сазе LexicalAnalyzer . RIGHT_PAREN : |
|||||
tname |
= |
") |
" ; |
||
break; |
|
|
|
|
|
сазе LexicalAnalyzer . QUOTED_STRING : |
||
tname |
= |
"quoted string" ; |
break ; |
|
|
сазе LexicalAnalyzer . EOF : |
||
tname |
= |
"end of file" ; |
break ; |
= |
"???" ; |
tname |
||
default : |
|
|
break ;
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6348x1.jpg)
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6349x1.jpg)
356 • Глава 8. Поведенческие шаблоны проектирования
сманипуляциями над объектами дерева синтаксического разбора. НеКОТОРые
более крупные языки требуют дополнительного анализа программы после Про ведения синтаксического разбора, с той целью, чтобы она могла быть ВЫпол няемоЙ. Для таких языков дерево синтаксического разбора представляет собой
промежуточную форму программы, отличную от ее выполняемой ФОРМЫ. Шаблон Little Language полагает, что язык достаточно прост для того, чтобы дe
рево синтаксического разбора можно было использовать двояко.
Приведем исходный код для класса NotWordComb i nation, который является простейшим подклассом класса Combinat ion:
class NotWordCombination extends Combination private Strinq word;
1 * *
* constructor
* @param word Слово в строке , которого требует эта * комбинация .
* /
NotWordCombination |
(Strinq word) |
this . word = |
word; |
} 1 1 cons tructor (String)
1 * *
*Если данная строка содержит слово, необходимое этому объекту NotWordCombinat ion ,
*то метод возвращает массив смещений,
*соответствующих появлению слова в строке .
* В противном случае этот метод возвращает null .
* 1 |
|
|
int [ ] |
contains (Strinq з) |
( |
if |
(s . indexOf (word) >= |
О) |
return null ; return new int [O] ;
1 1 conta i n s ( S tring)
11 class NotWordCombination
Класс отличие со
WordComb i na tion похож на класс Comb inat i on . Основное всех
стоит в том, что он содержит логику для возврата вектора смещений для
случаев появления в данной строке слова, связанного с объектом Wordcombi nati on.
Подклассы класса Combinati on, представляющие логические оператоРЬ!
OrCombination, AndComb ination И NearCombination, Я вляются более слоll'r
ными. Они отвечают за комбинирование результатов двух Объектов-потомКОВ
![](/html/2706/346/html_MxkKzTLKJz.Ja4Q/htmlconvd-LkZgZ6350x1.jpg)
Little Language • 357
combination. Приведем исходный код для AndCombination И OrCombi nat ion:
class AndComЬination extends ComЬination { private ComЬination leftChild, rightChild;
AndCombination (ComЬination leftChild , ComЬination rightChild)
this . leftChild = leftChild; this . rightChild = rightChild;
1 1 |
cons t ructor ( Combinat ion, Combinat i on ) |
||||
int [] |
contains (String |
з) |
{ |
|
|
int [ ] |
leftResult = |
leftChild . contains (s) ; |
|||
int [ ] |
rightResult = |
rightChild . contains (s) ; |
|||
if |
(leftResult == null |
I I rightResult == null) |
|||
|
return null; |
|
|
О) |
|
if (leftResult . length == |
|||||
|
return rightResult; |
|
О) |
||
if |
(rightResult. length == |
||||
|
return leftResult; |
|
|
||
1 1 |
Сортирует результаты с |
целью их сравнения и объединения . |
Sorter . sort (leftResult) ;
Sorter . sort (riqhtResult) ;
1 1 |
Подсчитывает общие |
смещения с целью выяснения , |
|
1 1 |
существуют ли общие |
смещения и сколько их . |
|
int conmonCount = О ; |
55 r<rightResult . length ; ) { |
||
for (int l=O , r=O ; |
|||
|
1<leftResult. length |
||
|
if (leftResult [l] < |
rightResult [r] ) { |
|
|
1++ ; |
if (leftResult [1] > rightResult [r] ) |
|
|
else |
||
|
r++ ; |
|
|
|
else |
{ |
|
|
commonCount++; |
|
|
|
1++ ; |
|
|
|
1 / i f |
|
|
|
r++; |
|
|
|
1 / for |
|
|
if |
(commonCount == О) |
|