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

Лаб. 4 ТАЯК

.docx
Скачиваний:
0
Добавлен:
25.01.2026
Размер:
140.45 Кб
Скачать

Лабораторная работа №4

Разработка транслятора заданных конструкций языка СИ++. Разработка синтаксического анализатора, обнаруживающего максимальное число ошибок

using System;

using System.Collections.Generic;

using System.IO;

using System.Linq;

// Типы токенов

public enum TokenType

{

// Ключевые слова

INT, BOOL, VOID, MAIN, FOR, IF, RETURN,

// Идентификаторы и литералы

IDENTIFIER, NUMBER,

// Операторы и разделители

ASSIGN, LT, GT, EQ, NE,

// Скобки и пунктуация

LPAREN, RPAREN, LBRACE, RBRACE, SEMICOLON, COMMA,

// Специальные токены

EOF, ERROR

}

// Класс для токена

public class Token

{

public TokenType Type { get; } // тип токена

public string Value { get; } // строковое значение токена

public int Line { get; } // позиция в коде: строка

public int Column { get; } // позиция в коде: столбец

public Token(TokenType type, string value, int line, int column)

{

Type = type;

Value = value;

Line = line;

Column = column;

}

public override string ToString()

{

return $"{Type}({Value}) at {Line}:{Column}";

}

}

// Лексический анализатор

public class Lexer

{

private readonly string _input;

private int _position;

private int _line;

private int _column;

private readonly List<Token> _tokens;

public Lexer(string input)

{

_input = input;

_position = 0;

_line = 1;

_column = 1;

_tokens = new List<Token>();

}

// Преобразование исходного кода в токены

public List<Token> Tokenize()

{

_tokens.Clear();

while (_position < _input.Length)

{

var current = Peek();

if (char.IsWhiteSpace(current))

{

SkipWhitespace();

}

else if (char.IsLetter(current) || current == '_')

{

ScanIdentifierOrKeyword();

}

else if (char.IsDigit(current))

{

ScanNumber();

}

else

{

ScanSymbol();

}

}

_tokens.Add(new Token(TokenType.EOF, "", _line, _column));

return _tokens;

}

// Просмотр текущего символа без смещения позиции

private char Peek(int offset = 0)

{

return _position + offset < _input.Length ? _input[_position + offset] : '\0';

}

// Чтение текущего символ со смещением позиции

private char Next()

{

var current = Peek();

if (_position < _input.Length)

{

_position++;

if (current == '\n')

{

_line++;

_column = 1;

}

else

{

_column++;

}

}

return current;

}

// Пропуск пробелов

private void SkipWhitespace()

{

while (_position < _input.Length && char.IsWhiteSpace(Peek()))

{

Next();

}

}

// Определение слов

private void ScanIdentifierOrKeyword()

{

var start = _position;

var startLine = _line;

var startColumn = _column;

while (_position < _input.Length && (char.IsLetterOrDigit(Peek()) || Peek() == '_'))

{

Next();

}

var value = _input.Substring(start, _position - start);

var type = value.ToLower() switch

{

"int" => TokenType.INT,

"bool" => TokenType.BOOL,

"void" => TokenType.VOID,

"main" => TokenType.MAIN,

"for" => TokenType.FOR,

"if" => TokenType.IF,

"return" => TokenType.RETURN,

_ => TokenType.IDENTIFIER

};

_tokens.Add(new Token(type, value, startLine, startColumn));

}

// Определение чисел

private void ScanNumber()

{

var start = _position;

var startLine = _line;

var startColumn = _column;

while (_position < _input.Length && char.IsDigit(Peek()))

{

Next();

}

var value = _input.Substring(start, _position - start);

_tokens.Add(new Token(TokenType.NUMBER, value, startLine, startColumn));

}

// Определение символов и операторов

private void ScanSymbol()

{

var startLine = _line;

var startColumn = _column;

var current = Next();

switch (current)

{

case '(':

_tokens.Add(new Token(TokenType.LPAREN, "(", startLine, startColumn));

break;

case ')':

_tokens.Add(new Token(TokenType.RPAREN, ")", startLine, startColumn));

break;

case '{':

_tokens.Add(new Token(TokenType.LBRACE, "{", startLine, startColumn));

break;

case '}':

_tokens.Add(new Token(TokenType.RBRACE, "}", startLine, startColumn));

break;

case ';':

_tokens.Add(new Token(TokenType.SEMICOLON, ";", startLine, startColumn));

break;

case '=':

if (Peek() == '=')

{

Next();

_tokens.Add(new Token(TokenType.EQ, "==", startLine, startColumn));

}

else

{

_tokens.Add(new Token(TokenType.ASSIGN, "=", startLine, startColumn));

}

break;

case '<':

_tokens.Add(new Token(TokenType.LT, "<", startLine, startColumn));

break;

case '>':

_tokens.Add(new Token(TokenType.GT, ">", startLine, startColumn));

break;

case '!':

if (Peek() == '=')

{

Next();

_tokens.Add(new Token(TokenType.NE, "!=", startLine, startColumn));

}

else

{

_tokens.Add(new Token(TokenType.ERROR, "!", startLine, startColumn));

}

break;

default:

_tokens.Add(new Token(TokenType.ERROR, current.ToString(), startLine, startColumn));

break;

}

}

}

// Класс ошибок

public class SyntaxError

{

public string Message { get; } // сообщение ошибки

public int Line { get; } // позиция в коде: строка

public int Column { get; } // позиция в коде: столбец

public string ErrorType { get; } // тип ошибки

public SyntaxError(string message, int line, int column, string errorType)

{

Message = message;

Line = line;

Column = column;

ErrorType = errorType;

}

public override string ToString()

{

return $"{ErrorType} на {Line}:{Column} - {Message}";

}

}

// Шаги анализа

public class AnalysisStep

{

public string Stack { get; }

public string Input { get; }

public string Output { get; }

public AnalysisStep(string stack, string input, string output)

{

Stack = stack;

Input = input;

Output = output;

}

}

// Таблица разбора (грамматика)

public class ParsingTable

{

private readonly Dictionary<string, Dictionary<TokenType, string[]>> _table;

public ParsingTable()

{

_table = new Dictionary<string, Dictionary<TokenType, string[]>>();

InitializeTable();

}

private void InitializeTable()

{

// Program → Type main ( ) { Statements }

AddEntry("Program", TokenType.INT, new[] { "Type", "main", "(", ")", "{", "Statements", "}" });

AddEntry("Program", TokenType.BOOL, new[] { "Type", "main", "(", ")", "{", "Statements", "}" });

AddEntry("Program", TokenType.VOID, new[] { "Type", "main", "(", ")", "{", "Statements", "}" });

// Type → int | bool | void

AddEntry("Type", TokenType.INT, new[] { "int" });

AddEntry("Type", TokenType.BOOL, new[] { "bool" });

AddEntry("Type", TokenType.VOID, new[] { "void" });

// Statements → Statement Statements | ε

AddEntry("Statements", TokenType.INT, new[] { "Statement", "Statements" });

AddEntry("Statements", TokenType.BOOL, new[] { "Statement", "Statements" });

AddEntry("Statements", TokenType.VOID, new[] { "Statement", "Statements" });

AddEntry("Statements", TokenType.LBRACE, new[] { "Statement", "Statements" });

AddEntry("Statements", TokenType.FOR, new[] { "Statement", "Statements" });

AddEntry("Statements", TokenType.IF, new[] { "Statement", "Statements" });

AddEntry("Statements", TokenType.RETURN, new[] { "Statement", "Statements" });

AddEntry("Statements", TokenType.RBRACE, new[] { "ε" });

AddEntry("Statements", TokenType.EOF, new[] { "ε" }); // Добавляем ε для EOF

// Statement → Declaration ; | { Statements } | For Statement | If Statement | Return

AddEntry("Statement", TokenType.INT, new[] { "Declaration", ";" });

AddEntry("Statement", TokenType.BOOL, new[] { "Declaration", ";" });

AddEntry("Statement", TokenType.VOID, new[] { "Declaration", ";" });

AddEntry("Statement", TokenType.LBRACE, new[] { "{", "Statements", "}" });

AddEntry("Statement", TokenType.FOR, new[] { "For", "Statement" });

AddEntry("Statement", TokenType.IF, new[] { "If", "Statement" });

AddEntry("Statement", TokenType.RETURN, new[] { "Return" });

// Declaration → Type id Assign

AddEntry("Declaration", TokenType.INT, new[] { "Type", "id", "Assign" });

AddEntry("Declaration", TokenType.BOOL, new[] { "Type", "id", "Assign" });

AddEntry("Declaration", TokenType.VOID, new[] { "Type", "id", "Assign" });

// Assign → = AssignEnd | ε

AddEntry("Assign", TokenType.ASSIGN, new[] { "=", "AssignEnd" });

AddEntry("Assign", TokenType.SEMICOLON, new[] { "ε" });

AddEntry("Assign", TokenType.RBRACE, new[] { "ε" });

// AssignEnd → id | num

AddEntry("AssignEnd", TokenType.IDENTIFIER, new[] { "id" });

AddEntry("AssignEnd", TokenType.NUMBER, new[] { "num" });

// For → for ( Declaration ; BoolExpression ; )

AddEntry("For", TokenType.FOR, new[] { "for", "(", "Declaration", ";", "BoolExpression", ";", ")" });

// BoolExpression → Expression Relop Expression

AddEntry("BoolExpression", TokenType.IDENTIFIER, new[] { "Expression", "Relop", "Expression" });

AddEntry("BoolExpression", TokenType.NUMBER, new[] { "Expression", "Relop", "Expression" });

// Expression → id | num

AddEntry("Expression", TokenType.IDENTIFIER, new[] { "id" });

AddEntry("Expression", TokenType.NUMBER, new[] { "num" });

// Relop → < | > | == | !=

AddEntry("Relop", TokenType.LT, new[] { "<" });

AddEntry("Relop", TokenType.GT, new[] { ">" });

AddEntry("Relop", TokenType.EQ, new[] { "==" });

AddEntry("Relop", TokenType.NE, new[] { "!=" });

// If → if ( BoolExpression )

AddEntry("If", TokenType.IF, new[] { "if", "(", "BoolExpression", ")" });

// Return → return num ;

AddEntry("Return", TokenType.RETURN, new[] { "return", "num", ";" });

}

private void AddEntry(string nonTerminal, TokenType tokenType, string[] production)

{

if (!_table.ContainsKey(nonTerminal))

_table[nonTerminal] = new Dictionary<TokenType, string[]>();

_table[nonTerminal][tokenType] = production;

}

public string[] GetProduction(string nonTerminal, TokenType tokenType)

{

if (_table.ContainsKey(nonTerminal) && _table[nonTerminal].ContainsKey(tokenType))

return _table[nonTerminal][tokenType];

return null;

}

}

// Синтаксический анализатор

public class PredictiveParser

{

private readonly List<Token> _tokens;

private int _position;

private readonly List<SyntaxError> _errors;

private readonly List<AnalysisStep> _analysisSteps;

private readonly Stack<string> _stack;

private readonly ParsingTable _parsingTable;

public PredictiveParser(List<Token> tokens)

{

_tokens = tokens;

_position = 0;

_errors = new List<SyntaxError>();

_analysisSteps = new List<AnalysisStep>();

_stack = new Stack<string>();

_parsingTable = new ParsingTable();

_stack.Push("$");

_stack.Push("Program");

}

public (bool Success, List<SyntaxError> Errors, List<AnalysisStep> Steps) Parse()

{

_errors.Clear();

_analysisSteps.Clear();

AddStep("Начало анализа");

while (_stack.Count > 0 && _stack.Peek() != "$")

{

var X = _stack.Peek();

var a = CurrentToken();

if (IsTerminal(X))

{

if (MatchesTerminal(X, a))

{

_stack.Pop();

_position++;

AddStep($"Совпадение: {X}");

}

else

{

ReportError($"Ожидается '{GetTerminalName(X)}', но найдено '{a.Value}'", a.Line, a.Column);

// Вместо просто удаления терминала, используем режим паники

if (!PanicModeRecoveryForTerminal(X, a))

{

_stack.Pop(); // Если не удалось восстановиться, удаляем терминал

}

}

}

else

{

var production = _parsingTable.GetProduction(X, a.Type);

if (production != null)

{

_stack.Pop();

if (production.Length == 1 && production[0] == "ε")

{

AddStep($"{X}: ε");

}

else

{

for (int i = production.Length - 1; i >= 0; i--)

{

if (production[i] != "ε")

{

_stack.Push(production[i]);

}

}

AddStep($"{X}: {string.Join(" ", production)}");

}

}

else

{

ReportError($"Синтаксическая ошибка: нет правила для {X} с токеном '{a.Value}'", a.Line, a.Column);

PanicModeRecovery(X);

}

}

}

// Проверяем, что достигли конца файла

if (_stack.Count > 0 && _stack.Peek() == "$" && CurrentToken().Type != TokenType.EOF)

{

ReportError("Неожиданные токены после конца программы", CurrentToken().Line, CurrentToken().Column);

}

AddStep("Анализ завершен");

return (_errors.Count == 0, _errors, _analysisSteps);

}

private bool PanicModeRecoveryForTerminal(string terminal, Token currentToken)

{

// Для некоторых терминалов можем попробовать вставить пропущенный токен

if (terminal == ";" && IsStatementStarter(currentToken.Type))

{

AddStep($"Восстановление: вставляем пропущенную ';'");

_stack.Pop(); // Удаляем ожидаемый ';' из стека

// Не двигаем позицию - currentToken будет обработан как начало нового statement

return true;

}

return false;

}

private bool IsStatementStarter(TokenType tokenType)

{

return tokenType == TokenType.INT || tokenType == TokenType.BOOL || tokenType == TokenType.VOID ||

tokenType == TokenType.FOR || tokenType == TokenType.IF || tokenType == TokenType.RETURN ||

tokenType == TokenType.LBRACE || tokenType == TokenType.RBRACE;

}

private Token CurrentToken()

{

return _position < _tokens.Count ? _tokens[_position] : _tokens[^1];

}

private string GetRemainingInput()

{

var remaining = _tokens.Skip(_position).Take(5);

return string.Join("", remaining.Select(t => t.Value)) +

(_tokens.Count - _position > 5 ? "..." : "");

}

private void AddStep(string output = "")

{

var stackStr = string.Join(" ", _stack.Reverse());

var inputStr = GetRemainingInput();

_analysisSteps.Add(new AnalysisStep(stackStr, inputStr, output));

}

private bool IsTerminal(string symbol)

{

return symbol switch

{

"int" or "bool" or "void" or "main" or "for" or "if" or "return" or

"id" or "num" or "=" or "<" or ">" or "==" or "!=" or

"(" or ")" or "{" or "}" or ";" => true,

_ => false

};

}

private bool MatchesTerminal(string terminal, Token token)

{

return terminal switch

{

"int" => token.Type == TokenType.INT,

"bool" => token.Type == TokenType.BOOL,

"void" => token.Type == TokenType.VOID,

"main" => token.Type == TokenType.MAIN,

"for" => token.Type == TokenType.FOR,

"if" => token.Type == TokenType.IF,

"return" => token.Type == TokenType.RETURN,

"id" => token.Type == TokenType.IDENTIFIER,

"num" => token.Type == TokenType.NUMBER,

"=" => token.Type == TokenType.ASSIGN,

"<" => token.Type == TokenType.LT,

">" => token.Type == TokenType.GT,

"==" => token.Type == TokenType.EQ,

"!=" => token.Type == TokenType.NE,

"(" => token.Type == TokenType.LPAREN,

")" => token.Type == TokenType.RPAREN,

"{" => token.Type == TokenType.LBRACE,

"}" => token.Type == TokenType.RBRACE,

";" => token.Type == TokenType.SEMICOLON,

_ => false

};

}

private string GetTerminalName(string terminal)

{

return terminal switch

{

"id" => "идентификатор",

"num" => "число",

_ => terminal

};

}

private void ReportError(string message, int line, int column)

{

_errors.Add(new SyntaxError(message, line, column, ""));

}

// Режим паники

private void PanicModeRecovery(string currentNonTerminal)

{

var syncTokens = GetSyncTokens(currentNonTerminal);

// Пропускаем токены до синхронизирующего

while (_position < _tokens.Count && !syncTokens.Contains(CurrentToken().Type))

{

AddStep($"Режим паники: пропускаем '{CurrentToken().Value}'");

_position++;

}

// Удаляем нетерминал из стека

_stack.Pop();

AddStep($"Режим паники: удаляем {currentNonTerminal} из стека");

}

// Получение синхронизирующих токенов для нетерминалов

private List<TokenType> GetSyncTokens(string nonTerminal)

{

return nonTerminal switch

{

"Program" => new List<TokenType> { TokenType.EOF },

"Statements" => new List<TokenType> { TokenType.RBRACE, TokenType.EOF },

"Statement" => new List<TokenType> { TokenType.SEMICOLON, TokenType.RBRACE, TokenType.EOF },

"Declaration" => new List<TokenType> { TokenType.SEMICOLON, TokenType.RBRACE, TokenType.EOF },

"Assign" => new List<TokenType> { TokenType.SEMICOLON, TokenType.RBRACE, TokenType.EOF },

"BoolExpression" => new List<TokenType> { TokenType.SEMICOLON, TokenType.RPAREN, TokenType.RBRACE, TokenType.EOF },

"Expression" => new List<TokenType> { TokenType.SEMICOLON, TokenType.RPAREN, TokenType.RBRACE, TokenType.EOF },

_ => new List<TokenType> { TokenType.SEMICOLON, TokenType.RBRACE, TokenType.EOF }

};

}

}

public class AnalysisTableGenerator

{

public static string GenerateAnalysisTable(List<AnalysisStep> steps)

{

// Определяем максимальные ширины для каждого столбца

int maxStackWidth = 0;

int maxInputWidth = 0;

int maxOutputWidth = 0;

foreach (var step in steps)

{

maxStackWidth = Math.Max(maxStackWidth, step.Stack.Length);

maxInputWidth = Math.Max(maxInputWidth, step.Input.Length);

maxOutputWidth = Math.Max(maxOutputWidth, step.Output.Length);

}

// Добавляем отступы для читаемости

maxStackWidth = Math.Max(maxStackWidth + 4, 30);

maxInputWidth = Math.Max(maxInputWidth + 4, 30);

maxOutputWidth = Math.Max(maxOutputWidth + 4, 40);

var table = "Стек".PadRight(maxStackWidth) + "Вход".PadRight(maxInputWidth) + "Выход/Примечание".PadRight(maxOutputWidth) + "\n";

table += new string('=', maxStackWidth + maxInputWidth + maxOutputWidth) + "\n";

foreach (var step in steps)

{

table += step.Stack.PadRight(maxStackWidth) +

step.Input.PadRight(maxInputWidth) +

step.Output.PadRight(maxOutputWidth) + "\n";

}

return table;

}

}

class Program

{

static void Main(string[] args)

{

string testProgram = @"int main() {

int a = 5

for (int a = 3; b > 7; {

int l = 7;

if (e == 3){

for (int w = 5; 1 != f; ){

int = 2;

}

}

bool e;

return 3;

}";

try

{

// Лексический анализ

var lexer = new Lexer(testProgram);

var tokens = lexer.Tokenize();

Console.WriteLine("Токены:");

foreach (var token in tokens)

{

if (token.Type != TokenType.EOF)

{

Console.WriteLine($" {token}");

}

}

Console.WriteLine();

// Синтаксический анализ с таблицей

var parser = new PredictiveParser(tokens);

var (success, errors, steps) = parser.Parse();

// Вывод результатов

Console.WriteLine($"Результат анализа: {(success ? "УСПЕШНЫЙ УСПЕХ!!!" : "ОШИБКА :(")}");

Console.WriteLine($"Количество ошибок: {errors.Count}\n");

if (errors.Count > 0)

{

Console.WriteLine("Обнаруженные ошибки:");

foreach (var error in errors)

{

Console.WriteLine($" {error}");

}

Console.WriteLine();

}

// Генерация таблицы анализа

string table = AnalysisTableGenerator.GenerateAnalysisTable(steps);

Console.WriteLine("Таблица анализа:");

Console.WriteLine(table);

//File.WriteAllText("analysis_table.txt", table);

//Console.WriteLine("Таблица анализа сохранена в файл: analysis_table.txt");

}

catch (Exception ex)

{

Console.WriteLine($"Критическая ошибка: {ex.Message}");

}

}

}

Соседние файлы в предмете Теория алгоритмических языков и компиляторов