- •Лингвистическое программное обеспечение сапр
- •Введение. Установка и настройка интегрированной среды для ведения разработки на языке Java.
- •Лабораторная работа 1. Анализатор скобочных алгебраических выражений.
- •Общая схема парсера
- •Стековая организация данных
- •Программирование анализатора скобочных выражений
Программирование анализатора скобочных выражений
По достижению данного этапа мы должны иметь законченных независимых класса. Класс стека с фиксированным буфером FixStack и класс, позволяющий анализировать строковое выражение TokensParser. Следующим этапом проектирования будет связывание этих двух классов. Этот этап будет включать создание класса-анализатора на основе стека с фиксированным буфером. Общая структура класса представлена ниже.
|
public class BracketParser implements TokenParser { private FixStack stack;
public BracketParser() { … }
public BracketParser(int size) { … }
public boolean parseToken(String source, int currentIndex) { … }
private boolean analyzeSymbol(char ch) { … }
private char pair(char ch) { … } } |
Листинг 1.9. Листинг конструкторов класса FixStack.
Первым делом, на что следует обратить внимание, это принадлежность данного класса к классу TokenParser, интерфейс которого мы определили раннее в классе TokenParsers. Это говорит нам о необходимости включить метод определённый в интерфейсе в данный класс-анализатор. Это метод parseToken. Но прежде чем рассматривать данный метод нужно инициализировать структуру стека, с которой мы будем работать. Это структура обязательно должна быть защищена от несанкционированного доступа, поэтому мы определяем её зарезервированным слово private. Имя поля прежде всегда должно определять сущность объекта, поэтому именем поля будет stack. По правилам объектно-ориентированного программирования инициализацию принято проводить в конструкторе класса. Поэтому прежде чем инициализировать структуру стека необходимо описать структуру конструктора.
Как видно из листинга 1.9 данный класса также имеет два конструктора. Один для инициализации при начальных условиях, второй для инициализации при заданных условиях. Листинг обоих методов представлен ниже. Очевидно, что внутри конструкторов должна быть инициализация класса стека с фиксированным буфером.
|
public BracketParser() { stack = new FixStack(); }
public BracketParser(int size) { stack = new FixStack(size); } |
Листинг 1.10. Листинг конструкторов класса BracketParser.
Категория доступа private позволяет обращатся к общедоступным методам подобъекта класса FixStack (push, pop, state) в методах класса BracketParser, но не в каких-либо других внешних функциях (например, main()). Так реализована защита класса стека от несанкционированного доступа. Если бы категория доступа была public, то это позволило обращаться к стеку не только там где он определён.
После определения главной структуры стека необходимо обратить к главному методу этого класса, который объясняет его сущность. Листинг данного метода изображён ниже. По ходу описания данного метода мы определим остальные методы данного класса.
|
public boolean parseToken(String source, int currentIndex) { boolean analyzeStatus; analyzeStatus = analyzeSymbol(source.charAt(currentIndex));
if (analyzeStatus) { if ( currentIndex+1 == source.length() && stack.getState() != 0 ) throw new IllegalArgumentException("В стэке лишняя скобка"); }
return analyzeStatus; } |
Листинг 1.11. Листинг метода parseToken.
Прежде всего данный метод получает строковую переменную source, которая содержит всё выражение целиком и переменную currentIndex, значение которой позволяет узнать а каком символе остановился предыдущий анализ. В блоке инициализации переменных метода определим переменную analyzeStatus. Она будет хранить промежуточные результаты анализа. Дальнейшее рассмотрение класса основывается методе, который собственно и анализирует текущий символ.
|
private boolean analyzeSymbol(char ch) { char buf;
switch (ch) { case '{': case '(': case'[': stack.push(ch); return true; сase '}': case ')': case ']': if ( stack.getState() == 0 ) { throw new IllegalArgumentException("Стэк пуст. Возможно лишняя закрывающаяся скобка"); } else buf = stack.pop();
if ( ch == pair(buf) ) { return true; } else { throw new IllegalArgumentException("Несоотвествие скобок. Возможно лишняя закрывающаяся скобка"); } } return false; } |
Листинг 1.12. Листинг метода analyzeSymbol.
Во первых необходимо инициализировать переменную, которая отвечает за хранение извелечённого элемента(buf). Далее следует общая структура, определяющаяся оператором условия switch. В теле данного оператора формально определено два типа скобок: открывающиеся и закрывающиеся. В случае если символ не соотвествует не одной из определённых выше скобок, метод возвращает логическое значение того, что анализ завершился неудачно. В случае же если полученный символ ch является скобкой, имее два дальнейших исхода. Первый – если скобка открывающаяся, метод заносит её в стек и возвращает логическое значение успешности анализа. Второй – если скобка закрывающаяся, следует проверка на непустоту буфера и при положительном исходе, сначала извлекается элемент из стека, а потом отыскивается его пара при помощи метода pair. Его листинг определён ниже. Если входной символ совпал с подобранной парой метод вернёт логическое значение true, говорящее об успешности анализа. В случае же пустого стека или несоотвествия пары скобок, предусмотрены соотвествующие исключения IllegalArgumentException, расширяющие классjava.lang. Они останавливают работу программы и выдают в консоль соотвествующиее строковое выражение.
|
private char pair(char ch) { if ( '(' == ch ) return ')'; else if ( '[' == ch ) return ']'; else if ( '{' == ch ) return '}'; return0; } |
Листинг 1.13. Листинг метода pair.
В итоге проделанной работы мы должны иметь 3 класса, для управления которыми мы создадим класс Main. Класс небольшой и интуитивно понятный поэтому в описании не нуждается.
|
public class Main { public static void main(String[] args) { TokensParser tp = new TokensParser(); tp.parse("(()"); } } |
Листинг 1.14. Листинг класса Main.
Заключение.
