Федеральное агентство по образованию Государственное общеобразовательное учреждение ТГТУ Отчет по лабораторной работе №1 Тема: “ РАЗРАБОТКА ЛЕКСИЧЕСКОГО АНАЛИЗАТОРА ” Выполнила: Студентка 4 курса Специальности ПОВТ-0906 Павленко Людмила Принял: Калабин А.Л. Тверь, 2012 Отчет по лабораторной работе №1 Тема: «РАЗРАБОТКА ЛЕКСИЧЕСКОГО АНАЛИЗАТОРА» Целью данной работы является написание блока сканирования, который считывает исходную программу и представляет ее в форме файла лексем. Чтение файла в строку. Для выбора исходного файла в структуре файловой системы используется элемент управления OpenFileDialog. Метод ShowDialog() отобразит диалог выбора файла на экран, а о том, какую кнопку в диалоге нажал пользователь можно узнать, сравнив значение, возвращенное методом с константами статического класса DialogResult. При значении, равном DialogResult.OK путь к выбранному файлу в файловой системе можно получить через свойство FileName экземпляра OpenFileDialog. Это значение нужно передать в метод ReadAllText класса File. Метод вернет нам содержимое исходного файла. Запись строки в файл. Необходимо использовать метод WriteAllText класса File. В качестве параметров ему передаются имя файла и исходная строка. Широко распространенной практикой является проверка на существование файла перед записью с помощью метода Exists класса File. Посимвольное чтение из файла. Класс StreamReader предоставляет методы чтения следующего символа, строки или блока в файле. Однако на каждом шаге необходимо сохранять данные о текущей строке, текущем символе и позиции символа в строке. Довольно логичным будет реализовать статический класс-обертку над StreamReader, который обеспечил бы нам не только чтение из файла,но и хранение этих данных. Вариант 10 (БНФ) <Программа> ::= <Объявление переменных> <Описание вычислений> <Оператор печати> <Описание вычислений> ::= Begin <Список присваиваний> End <Объявление переменных> ::= Integer <Список переменных> <Список переменных> ::= <Идент>; | <Идент> , <Список переменных> <Список присваиваний>::= <Присваивание> | <Присваивание> <Список присваиваний> <Присваивание> ::= <Идент> := <Выражение> ; <Выражение> ::= <Ун.оп.> <Подвыражение> | <Подвыражение> <Подвыражение> :: = ( <Выражение> ) | <Операнд> | < Подвыражение > <Бин.оп.> <Подвыражение> <Ун.оп.> ::= "-" <Бин.оп.> ::= "-" | "+" | "*" | "/" <Операнд> ::= <Идент> | <Const> <Идент> ::= <Буква> <Идент> | <Буква> <Const> ::= <Цифра> <Const> | <Цифра> <Оператор печати>::=Print <Идент> На одной строке может быть только объявление переменных или один оператор присваивания Нетерминалы: N= { S=<Программа> D=<Объявление переменных> F=<Описание вычислений> P=<Оператор печати> V=<Список переменных> I=<Идентификатор> G=<Список присваиваний> A=<Присваивание> E=<Выражение> H=<Подвыражение> O=<Операнд> C=<Const> N=<Цифра> } Терминалы: Ключевые слова языка: T={begin, end, integer, print} Разделители: R={:=, .();} Алфавит: A={a|…|z} Бинарные операции: B={+|-|/|*} Унарная опреация: U={-} Цифры: C={0|…|9} Правила: S=DF F=begin G end G=A|AG A=I:=E I=LI|L E=UH|H H=(E)|O|HBH O=I|C C=NC|N D=integer V V=I;|I,V P=print I Листинг программы: /// <summary> /// Класс анализатор /// Содержит символы разделения значений и конца строки /// </summary> public class Analiser { /// <summary> /// Символ разделения параметров /// </summary> protected const String SEPARATOR = ","; /// <summary> /// Символ конца строки /// </summary> protected const String END_LINE = ";"; /// <summary> /// Пробел /// </summary> protected const String BLANK = " "; /// <summary> /// Установление значение /// </summary> protected const String EQUATION = ":="; /// <summary> /// Таблица ключевых слов /// </summary> protected String[] TABLE_KEY_WORDS = { "const", "integer", "begin", "end", "print" }; /// <summary> /// Бинарная операция "Плюс" /// </summary> protected const String PLUS = "+"; /// <summary> /// Бинарная операция "Минус" /// </summary> protected const String MINUS = "-"; /// <summary> /// Бинарная операция "Умножение" /// </summary> protected const String MULTI = "*"; /// <summary> /// Бинарная операция "Деление" /// </summary> protected const String DIVISION = "/"; } public class LexemeAnaliser:Analiser,ILexemeAnalyser { private LexemeList lexemeList; /// <summary> /// Объект "представление" /// </summary> private TranslatorView view; public LexemeAnaliser(TranslatorView view) { lexemeList = new LexemeList(); this.view = view; } public void anlisys() { try { lexemeList = new LexemeList(); for (int numberLine = 0; numberLine < LoaderManagerController.InputList.Count; numberLine++) { if (LoaderManagerController.InputList[numberLine].Equals(TABLE_KEY_WORDS[3])) { //numberLine = LoaderManagerController.InputList.Count; view.Log = "Variables was initialized"; break; } if (LoaderManagerController.InputList[numberLine].Equals(TABLE_KEY_WORDS[5])) continue; addLexemeInList(numberLine,LoaderManagerController.InputList[numberLine], getTypeLexemeValue(LoaderManagerController.InputList[numberLine])); } } catch(Exception ex) { view.Log = ex.Message; } } /// <summary> /// Возвращает тип объекта лексемы /// </summary> /// <param name="line">Строка для сканирования</param> /// <returns>Тип объекта лексемы</returns> private String getTypeLexemeValue(string line) { String value; try { value = line.Remove(line.IndexOf(BLANK), line.Length line.IndexOf(BLANK)).Trim(); } catch { throw new Exception("Невозможно получить тип лексемы"); } return value; } /// <summary> /// Получить все переменные объявленныу для данного идентификатора/ константы /// </summary> /// <param name="line">Строка для анализа</param> /// <param name="typeLexeme">Тип идентификатора</param> /// <returns>Список обяъвленных переменных</returns> private String[] getVariables(String line, String typeLexeme) { string[] variables = null; line = line.Remove(line.IndexOf(typeLexeme), typeLexeme.Length); //Удаляем тип переменных line = line.Remove(line.IndexOf(END_LINE), END_LINE.Length); //Удаляем символ конца строки line = line.Trim(); variables = line.Split(SEPARATOR.ToCharArray()[0]); return variables; } /// <summary> /// Получить значение лексемы /// </summary> /// <param name="lexeme">Лексема</param> /// <returns>Значение лексемы</returns> private Object getValueLexeme(String lexeme) { try { if (lexeme.IndexOf(EQUATION) == -1) return null; lexeme = lexeme.Remove(0, lexeme.Length lexeme.IndexOf(EQUATION)); return lexeme.Trim(); } catch { throw new Exception("Ошибка при получении значения лексемы"); } } /// <summary> /// Получить имя лексемы /// </summary> /// <param name="lexeme">Лексема</param> /// <returns>Имя лексемы</returns> private String getNameLexeme(String lexeme) { try { if (lexeme.IndexOf(EQUATION) == -1) return lexeme.Trim(); lexeme = lexeme.Remove(lexeme.IndexOf(EQUATION),lexeme.Length - lexeme.IndexOf(EQUATION)); return lexeme.Trim(); } catch { throw new Exception("Ошибка при получении имени лексемы"); } } private void addLexemeInList(int numberLine, String line, String typeLexeme) { switch (typeLexeme) { case "int": { string[] variables = getVariables(line, typeLexeme); for (int numberOnLine = 0; numberOnLine < variables.Length; numberOnLine++) { lexemeList.add(new Identificator(getNameLexeme(variables[numberOnLine]), getTypeLexemeValue(line), numberOnLine, numberLine, getValueLexeme(variables[numberOnLine]))); } break; } case "double": { string[] variables = getVariables(line, typeLexeme); for (int numberOnLine = 0; numberOnLine < variables.Length; numberOnLine++) { lexemeList.add(new Identificator(getNameLexeme(variables[numberOnLine]), getTypeLexemeValue(line), numberOnLine, numberLine, getValueLexeme(variables[numberOnLine]))); } break; } case "logical": { string[] variables = getVariables(line, typeLexeme); for (int numberOnLine = 0; numberOnLine < variables.Length; numberOnLine++) { lexemeList.add(new Identificator(getNameLexeme(variables[numberOnLine]), getTypeLexemeValue(line), numberOnLine, numberLine, getValueLexeme(variables[numberOnLine]))); } break; } case "const": { //Костыль конечно, но на первое время сойдет line = line.Remove(line.IndexOf("const"), "const".Length); line = line.Trim(); string[] variables = getVariables(line, getTypeLexemeValue(line)); for (int numberOnLine = 0; numberOnLine < variables.Length; numberOnLine++) { lexemeList.add(new Const(getNameLexeme(variables[numberOnLine]), getTypeLexemeValue(line), numberOnLine, numberLine, getValueLexeme(variables[numberOnLine]))); } break; } default: { throw new Exception("Неизвестная лексема '" + typeLexeme + "'"); } } } public LexemeList LexemesList { get { return this.lexemeList; } } } public abstract class AbstractLexeme:ILexeme { protected String id; protected Int32 positionInLine; protected Int32 numberLine; /// <summary> /// Тип лексемы /// </summary> protected LexemeType.Type TYPE_LEXEME; /// <summary> /// Тип значения лексемы /// </summary> protected String typeLexemeValue; protected Object value_; public override bool Equals(object obj) { if (obj is AbstractLexeme) { if (value_ == obj) return true; else return false; } else { throw new Exception("Error type"); } } public LexemeType.Type getType() { return TYPE_LEXEME; } public override string ToString() { String value = numberLine.ToString() + " " + positionInLine.ToString() + " " + id + " " + TYPE_LEXEME.ToString() + " " + typeLexemeValue + " "; if (value_ != null) value += value_.ToString(); else value += "null"; return value; } public String ID { get { return id; } set { throw new Exception("Can't setting value of field"); } } public Int32 PositionInLine { get { return positionInLine; } set { positionInLine = value; } } public Int32 NumberLine { get { return numberLine; } set { numberLine = value; } } public Object Value { get { return value_; } set { value_ = value; } } public String TypeLexemeValue { get { return typeLexemeValue; } set { typeLexemeValue = value; } } } Пример работы программы: Вывод: В результате проделанной работы были изучены основные понятия теории регулярных грамматик, назначения и принципы работы лексических анализаторов (сканеров). Были получены практические навыки построения сканера на примере заданного простейшего входного языка.