Мячина_5ЛР
.docxМИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ, СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ
Ордена Трудового Красного Знамени федеральное государственное бюджетное образовательное учреждение высшего образования
Московский технический университет связи и информатики
(МТУСИ)
Кафедра «Системное программирование»
Лабораторная работа №5
«Проектирование лексического анализатора»
по дисциплине «Системное программное обеспечение»
Вариант 8
Выполнила:
студентка группы БФИ2203
Мячина Лада Андреевна
Проверила:
Старший преподаватель
Алексанян Диана Ашотовна
Москва, 2025 г.
Задание: написать программу, которая выполняет лексический анализ входного текста в соответствии с заданием и порождает таблицу лексем с указанием их типов и значений. Текст на входном языке задается в виде символьного (текстового) файла. Программа должна выдавать сообщения о наличии во входном тексте ошибок, которые могут быть обнаружены на этапе лексического анализа с указанием места ошибки в исходном тексте программы.
Задание варианта 4: входной язык содержит операторы цикла типа for (…; …; …) do, разделенные символом; (точка с запятой). Операторы цикла содержат идентификаторы, знаки сравнения, =, десятичные числа с плавающей точкой (в обычной и логарифмической форме), знак присваивания (:=).
Ход работы:
Форма Бэкуса-Наура для входного языка:
<программа> ::= <оператор> (';' <оператор>)*
<оператор> ::= <цикл_for> | <комментарий>
<цикл_for> ::= 'for' '(' [<инициализация>] ';' [<условие>] ';' [<приращение>] ')' 'do'
<инициализация> ::= <присваивание>
<условие> ::= <выражение> <оператор_сравнения> <выражение>
<приращение> ::= <присваивание>
<присваивание> ::= <идентификатор> ':=' <выражение>
<выражение> ::= <идентификатор> | <римское_число>
<оператор_сравнения> ::= '<' | '>' | '='
<римское_число> ::= ('I' | 'V' | 'X')+
<идентификатор> ::= [a-zA-Z_][a-zA-Z0-9_]{0,31}
<комментарий> ::= '{' <текст_комментария> '}'
<текст_комментария> ::= [^}]*
Основной алгоритм:
Инициализация - установка начальных позиций (строка=1, позиция=1)
Чтение символов - последовательное чтение входного текста
Пропуск пробелов - игнорирование пробелов, табуляций, переносов строк
Определение типа лексемы по первому символу:
{ - начало комментария
Буква или _ - идентификатор/ключевое слово/римское число
: - оператор присваивания
<, >, = - оператор сравнения
(, ), ; - разделители
Разбор лексемы соответствующим методом
Добавление в таблицу лексем
Проверка ошибок и вывод сообщений
Повтор до конца файла
Листинг программы для выполнения ЛР:
import re
import sys
# Типы лексем
class TokenType:
FOR = 'FOR'
DO = 'DO'
IDENTIFIER = 'IDENTIFIER'
ROMAN_NUMERAL = 'ROMAN_NUMERAL'
ASSIGN = 'ASSIGN'
COMPARISON = 'COMPARISON'
LPAREN = 'LPAREN'
RPAREN = 'RPAREN'
SEMICOLON = 'SEMICOLON'
COMMENT = 'COMMENT'
ERROR = 'ERROR'
# Класс для представления лексемы
class Token:
def __init__(self, type, value, line, position):
self.type = type
self.value = value
self.line = line
self.position = position
def __str__(self):
return f"{self.type:<15} {self.value:<10} at line {self.line}, pos {self.position}"
class Lexer:
def __init__(self, text):
self.text = text
self.pos = 0
self.line = 1
self.line_pos = 1
self.tokens = []
self.keywords = {'for': TokenType.FOR, 'do': TokenType.DO}
self.comparison_ops = {'<', '>', '='}
self.roman_symbols = {'I', 'V', 'X'}
def error(self, message):
print(f"Lexical error at line {self.line}, position {self.line_pos}: {message}")
return Token(TokenType.ERROR, message, self.line, self.line_pos)
def get_next_char(self):
if self.pos >= len(self.text):
return None
char = self.text[self.pos]
self.pos += 1
if char == '\n':
self.line += 1
self.line_pos = 1
else:
self.line_pos += 1
return char
def peek_next_char(self):
if self.pos >= len(self.text):
return None
return self.text[self.pos]
def skip_whitespace(self):
while self.pos < len(self.text) and self.text[self.pos].isspace():
self.get_next_char()
def parse_comment(self):
start_line = self.line
start_pos = self.line_pos
comment = ''
char = self.get_next_char() # Пропускаем открывающую {
while True:
if char is None:
return self.error("Unclosed comment")
if char == '}':
break
comment += char
char = self.get_next_char()
return Token(TokenType.COMMENT, comment, start_line, start_pos)
def parse_identifier_or_roman(self):
start_line = self.line
start_pos = self.line_pos
value = ''
char = self.peek_next_char()
while char is not None and (char.isalnum() or char == '_'):
value += self.get_next_char()
char = self.peek_next_char()
# Проверяем是否是罗马数字
if all(c in self.roman_symbols for c in value):
return Token(TokenType.ROMAN_NUMERAL, value, start_line, start_pos)
# Проверяем是否是ключевоеслово
elif value.lower() in self.keywords:
return Token(self.keywords[value.lower()], value, start_line, start_pos)
else:
if len(value) > 32:
self.error(f"Identifier too long: {value}")
return Token(TokenType.IDENTIFIER, value, start_line, start_pos)
def parse_assignment(self):
start_line = self.line
start_pos = self.line_pos
self.get_next_char() # Пропускаем :
next_char = self.get_next_char()
if next_char == '=':
return Token(TokenType.ASSIGN, ':=', start_line, start_pos)
else:
return self.error("Expected '=' after ':'")
def tokenize(self):
while self.pos < len(self.text):
self.skip_whitespace()
if self.pos >= len(self.text):
break
current_char = self.peek_next_char()
if current_char == '{':
token = self.parse_comment()
if token.type != TokenType.ERROR:
self.tokens.append(token)
elif current_char.isalpha() or current_char == '_':
token = self.parse_identifier_or_roman()
self.tokens.append(token)
elif current_char == ':':
token = self.parse_assignment()
self.tokens.append(token)
elif current_char in self.comparison_ops:
start_line = self.line
start_pos = self.line_pos
op = self.get_next_char()
self.tokens.append(Token(TokenType.COMPARISON, op, start_line, start_pos))
elif current_char == '(':
start_line = self.line
start_pos = self.line_pos
self.get_next_char()
self.tokens.append(Token(TokenType.LPAREN, '(', start_line, start_pos))
elif current_char == ')':
start_line = self.line
start_pos = self.line_pos
self.get_next_char()
self.tokens.append(Token(TokenType.RPAREN, ')', start_line, start_pos))
elif current_char == ';':
start_line = self.line
start_pos = self.line_pos
self.get_next_char()
self.tokens.append(Token(TokenType.SEMICOLON, ';', start_line, start_pos))
else:
token = self.error(f"Unexpected character: {current_char}")
self.tokens.append(token)
self.get_next_char()
return self.tokens
def main():
if len(sys.argv) != 2:
print("Usage: python lexer.py <input_file>")
return
try:
with open(sys.argv[1], 'r', encoding='utf-8') as f:
text = f.read()
except FileNotFoundError:
print(f"Error: File {sys.argv[1]} not found")
return
lexer = Lexer(text)
tokens = lexer.tokenize()
print("Tokens found:")
print("-" * 50)
for token in tokens:
print(token)
if __name__ == "__main__":
main()
Файл без ошибок
Файл с ошибками
