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

Лаб. 3 ТАЯК

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

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

Недетерминированные магазинные автоматы

using System;

using System.Collections.Generic;

using System.IO;

using System.Linq;

class Production

{

public char Left; // Нетерминал, один символ (заглавная буква)

public string Right; // Правая часть как последовательность символов (каждый символ — либо нетерминал (A-Z), либо терминал)

public Production(char left, string right) { Left = left; Right = right; }

}

class PDA

{

// Дно магазина

public char h0 = '#';

// Начальное состояние

public string state = "s0";

// Нетерминалы (заглавные, левая часть правил)

public HashSet<char> VN = new HashSet<char>();

// Терминалы (входной алфавит)

public HashSet<char> VT = new HashSet<char>();

public List<Production> productions = new List<Production>();

public char startSymbol;

public void ParseGrammar(string[] lines)

{

bool firstLhsSeen = false;

foreach (var raw in lines)

{

string line = raw;

//var idx = line.IndexOf("//");

//if (idx >= 0) line = line.Substring(0, idx);

// Удаление пробелов вне апострофов

line = RemoveSpacesOutsideQuotes(line);

if (string.IsNullOrWhiteSpace(line)) continue;

// Найти первый символ '>'

int gt = line.IndexOf('>');

if (gt < 0) continue;

string lhs = line.Substring(0, gt);

string rhsAll = line.Substring(gt + 1);

if (lhs.Length == 0) continue;

char L = lhs.Trim()[0];

if (!firstLhsSeen) { startSymbol = L; firstLhsSeen = true; }

VN.Add(L);

// Обработка кавычек: ' ' -> ~, а 'x' -> x

rhsAll = UnquoteTerminals(rhsAll);

var rhsList = SplitRespectingQuotes(rhsAll, '|');

foreach (var rhs in rhsList)

{

//string right = rhs;

// Заносятся правила

productions.Add(new Production(L, rhs));

foreach (char c in rhs)

{

if (IsNonTerminal(c)) VN.Add(c);

else VT.Add(c);

}

}

}

}

static string UnquoteTerminals(string s)

{

// "0|1|' '|a" -> "0|1|~|a"

var outChars = new List<char>();

int i = 0;

while (i < s.Length)

{

if (s[i] == '\'')

{

// Поиск закрывающей кавычки

int j = i + 1;

while (j < s.Length && s[j] != '\'') j++;

if (j < s.Length && s[j] == '\'')

{

// Найдена закрывающая кавычка: взятие содержимого между ними

string inside = s.Substring(i + 1, j - (i + 1));

if (inside.Length == 1 && inside[0] == ' ')

{

outChars.Add('~'); // Использование '~' как маркер пробела

}

else if (inside.Length >= 1)

{

foreach (char c in inside) outChars.Add(c);

}

else

{

// Пустые кавычки '' — как сам символ апостроф (одиночный апостроф)

outChars.Add('\'');

}

i = j + 1;

}

else

{

// Нет закрывающей кавычки — считается одиночный апостроф как терминал '

outChars.Add('\'');

i++; // Продвижение на 1 символ

}

}

else

{

outChars.Add(s[i]);

i++;

}

}

return new string(outChars.ToArray());

}

static bool IsNonTerminal(char c) => c >= 'A' && c <= 'Z';

static string RemoveSpacesOutsideQuotes(string s)

{

bool inQuote = false;

var outChars = new List<char>();

foreach (char c in s)

{

if (c == '\'') { inQuote = !inQuote; outChars.Add(c); continue; }

if (!inQuote && char.IsWhiteSpace(c)) continue;

outChars.Add(c);

}

return new string(outChars.ToArray());

}

static List<string> SplitRespectingQuotes(string s, char sep)

{

var list = new List<string>();

bool inQuote = false;

var cur = new List<char>();

foreach (char c in s)

{

if (c == '\'') { inQuote = !inQuote; cur.Add(c); continue; }

if (!inQuote && c == sep)

{

list.Add(new string(cur.ToArray()));

cur.Clear();

}

else cur.Add(c);

}

list.Add(new string(cur.ToArray()));

return list;

}

static string ReverseString(string s)

{

var arr = s.ToCharArray();

Array.Reverse(arr);

return new string(arr);

}

static string PrintableChar(char c)

{

if (c == ' ') return "' '";

// апостроф теперь отображается как просто одинарная кавычка

if (c == '\'') return "'";

if (c == '~') return "~";

return c.ToString();

}

static string PrintableStackString(string s)

{

if (s == null || s.Length == 0) return "λ";

return string.Concat(s.Select(ch => PrintableChar(ch)));

}

public void PrintAll()

{

// 1) Множества

Console.WriteLine("Определение множеств:");

Console.WriteLine($" S = {{s0}}");

Console.WriteLine($" P (терминалы) = {{{string.Join(", ", VT.OrderBy(c => c))}}}");

var Z = new HashSet<char>(VN.Union(VT));

Z.Add(h0);

Console.WriteLine($" Z (алфавит магазина) = {{{string.Join(", ", Z.OrderBy(c => c))}}} (h0 обозначен символом '{h0}')");

Console.WriteLine($" F = {{s0}}");

Console.WriteLine();

// 2) Команды типа (1) — сгруппированы по LHS

Console.WriteLine("Команды типа (1) (для каждой продукции A->α):");

var groups = productions.GroupBy(p => p.Left).OrderBy(g => g.Key);

foreach (var g in groups)

{

string label = (g.Key == startSymbol) ? "ε" : "λ"; // ε для стартового, λ для остальных

var opts = g.Select(p => $"(s0 , {PrintableStackString(ReverseString(p.Right))})");

var rules = g.Select(p => PrintableStackString(p.Right));

Console.WriteLine($" δ0(s0 , {label} , {g.Key}) = {{ {string.Join(" ; ", opts)} }} // из правил {g.Key} -> {string.Join(" | ", rules)}");

}

Console.WriteLine();

// 3) Команды типа (2) и (3)

Console.WriteLine("Команды типа (2) (для каждого терминала a):");

foreach (var a in VT.OrderBy(c => c))

{

Console.WriteLine($" δ(s0 , {PrintableChar(a)} , {PrintableChar(a)}) = (s0 , λ)");

}

Console.WriteLine();

Console.WriteLine("Команда типа (3) для удаления маркера дна:");

Console.WriteLine($" δ(s0 , λ , {h0}) = (s0 , λ)");

Console.WriteLine();

}

class Config

{

public string state;

// Оставшаяся входная цепочка

public string input;

// Магазин

public string stack;

// Цепочка конфигураций (для вывода)

public List<string> history;

public Config(string state, string input, string stack, List<string> history)

{

this.state = state; this.input = input; this.stack = stack; this.history = history;

}

}

public List<string> FindAcceptingRun(string w, int maxConfigs)

{

string initialStack = h0 + startSymbol.ToString(); // Слева дно (h0), справа вершина (startSymbol)

var start = new Config(state, w, initialStack, new List<string> { $"({state}, {(w == "" ? "λ" : w)}, {PrintableStackString(initialStack)})" });

var q = new Queue<Config>();

q.Enqueue(start);

var visited = new HashSet<string>();

int configsExamined = 0;

while (q.Count > 0)

{

if (++configsExamined > maxConfigs) break;

var cur = q.Dequeue();

string key = cur.state + "|" + cur.input + "|" + cur.stack;

if (visited.Contains(key)) continue;

visited.Add(key);

if (cur.stack.Length == 0 && cur.input.Length == 0) return cur.history;

if (cur.stack.Length == 0) continue;

char top = cur.stack[cur.stack.Length - 1]; // Вершина — последний символ

string rest = cur.stack.Substring(0, cur.stack.Length - 1);

// 1) Если вершина — нетерминал

if (IsNonTerminal(top))

{

var applicable = productions.Where(p => p.Left == top).ToList();

foreach (var p in applicable)

{

string replacement = ReverseString(p.Right); // a^R

string newStack = rest + replacement;

var newHist = new List<string>(cur.history);

newHist.Add($"({state}, {(cur.input == "" ? "λ" : cur.input)}, {PrintableStackString(newStack)}) // Замена {top} на {PrintableStackString(p.Right)}");

q.Enqueue(new Config(state, cur.input, newStack, newHist));

}

}

// 2) Если вершина — терминал и совпадает с головкой входа

if (cur.input.Length > 0 && !IsNonTerminal(top))

{

char inputHead = cur.input[0];

if (top == inputHead)

{

string restStack = rest;

string restInput = cur.input.Length > 1 ? cur.input.Substring(1) : "";

var newHist = new List<string>(cur.history);

newHist.Add($"({state}, {(restInput == "" ? "λ" : restInput)}, {PrintableStackString(restStack)}) // Удаление терминала {PrintableChar(top)}");

q.Enqueue(new Config(state, restInput, restStack, newHist));

}

}

// 3) Если вершина — h0 и вход пуст, то удаляется h0

if (top == h0 && cur.input.Length == 0)

{

string restStack = rest;

var newHist = new List<string>(cur.history);

newHist.Add($"({state}, λ, {PrintableStackString(restStack)}) // Удаление h0");

q.Enqueue(new Config(state, cur.input, restStack, newHist));

}

}

return null;

}

}

class Program

{

static void Main(string[] args)

{

Console.OutputEncoding = System.Text.Encoding.UTF8;

Console.WriteLine("Построение недетерминированного магазинного автомата по грамматике и проверка цепочки.\n");

Console.Write("Путь к файлу с переходами (Enter — пример): ");

string path = Console.ReadLine()?.Trim();

string[] lines;

if (string.IsNullOrEmpty(path))

{

lines = new string[] {

"E>E+T|T",

"T>T*F|F",

"F>(E)|a"

};

Console.WriteLine("Используется встроенный пример:");

foreach (var l in lines) Console.WriteLine(l);

Console.WriteLine();

}

else

{

if (!File.Exists(path))

{

Console.WriteLine("Файл не найден. Завершение.");

return;

}

lines = File.ReadAllLines(path);

}

var pda = new PDA();

pda.ParseGrammar(lines);

pda.PrintAll();

Console.Write("Введите входную цепочку (используйте '~' для пробела, если нужно): ");

string w = Console.ReadLine() ?? "";

w = w.Replace(" ", "");

Console.WriteLine($"\nНачальная конфигурация (4): ({pda.state}, {(w == "" ? "λ" : w)}, {pda.h0}{pda.startSymbol})");

Console.WriteLine("Идёт поиск допускающей последовательности конфигураций...");

int maxConfigs = 200000;

var run = pda.FindAcceptingRun(w, maxConfigs);

Console.WriteLine();

if (run != null)

{

Console.WriteLine("Найдена допускающая последовательность конфигураций:");

foreach (var s in run) Console.WriteLine(s);

Console.WriteLine("\nВывод: цепочка допускается магазинным автоматом.");

}

else

{

Console.WriteLine("Не найдено допускающей последовательности конфигураций в пределах лимита поиска.");

Console.WriteLine("Вывод: цепочка НЕ допускается автоматом (или поиск не завершился из-за долгой продолжительности).");

}

}

}

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