Лаб. 3 ТАЯК
.docxЛабораторная работа №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("Вывод: цепочка НЕ допускается автоматом (или поиск не завершился из-за долгой продолжительности).");
}
}
}
