add: data-structure-lab & compiler-lab
This commit is contained in:
338
GrammarParser/LlParser.cpp
Normal file
338
GrammarParser/LlParser.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <list>
|
||||
|
||||
#define EMPTY L'ε'
|
||||
#define END L'$'
|
||||
|
||||
struct Grammar
|
||||
{
|
||||
std::unordered_map<wchar_t, std::vector<std::wstring>> Generators;
|
||||
std::unordered_set<wchar_t> Nonterminators;
|
||||
wchar_t Begin;
|
||||
std::unordered_map<wchar_t, std::unordered_set<wchar_t>> FirstSet;
|
||||
std::unordered_map<wchar_t, std::unordered_set<wchar_t>> FollowSet;
|
||||
std::unordered_map<wchar_t, std::unordered_map<wchar_t, std::wstring>> AnalysisTable;
|
||||
|
||||
Grammar(const std::unordered_map<wchar_t, std::vector<std::wstring>>&generators, const wchar_t begin)
|
||||
{
|
||||
Generators = generators;
|
||||
Begin = begin;
|
||||
|
||||
for (const auto&[key, value]: generators)
|
||||
{
|
||||
Nonterminators.emplace(key);
|
||||
FirstSet.emplace(key, std::unordered_set<wchar_t>());
|
||||
FollowSet.emplace(key, std::unordered_set<wchar_t>());
|
||||
AnalysisTable.emplace(key, std::unordered_map<wchar_t, std::wstring>());
|
||||
}
|
||||
|
||||
// 求非终结符的FIRST集合
|
||||
bool changed = true;
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
|
||||
for (const auto&[key, value]: generators)
|
||||
{
|
||||
for (const auto&expression: value)
|
||||
{
|
||||
if (Nonterminators.count(expression.front()) != 0)
|
||||
{
|
||||
// 合并其他非终结符的FIRST集合
|
||||
for (const auto&c: FirstSet[expression.front()])
|
||||
{
|
||||
if (c != EMPTY and FirstSet[key].count(c) == 0)
|
||||
{
|
||||
FirstSet[key].emplace(c);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FirstSet[key].count(expression.front()) == 0)
|
||||
{
|
||||
FirstSet[key].emplace(expression.front());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 求非终结符的FOLLOW集合
|
||||
changed = true;
|
||||
FollowSet[begin].emplace(END);
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
|
||||
for (const auto&[key, value]: generators)
|
||||
{
|
||||
for (const auto&expression: value)
|
||||
{
|
||||
for (auto i = expression.begin(); i != expression.end(); ++i)
|
||||
{
|
||||
if (Nonterminators.count(*i) != 0)
|
||||
{
|
||||
// 发现非终结符
|
||||
auto next = i;
|
||||
++next;
|
||||
|
||||
if (next == expression.end())
|
||||
{
|
||||
// 非终结符在表达式末尾
|
||||
for (const auto&c: FollowSet[key])
|
||||
{
|
||||
if (FollowSet[*i].count(c) == 0)
|
||||
{
|
||||
FollowSet[*i].emplace(c);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
std::wstring newExpression;
|
||||
while (next != expression.end())
|
||||
{
|
||||
newExpression += *next;
|
||||
++next;
|
||||
}
|
||||
|
||||
const auto&firstSet = GetFirstSet(newExpression);
|
||||
|
||||
for (const auto&c: firstSet)
|
||||
{
|
||||
if (c == EMPTY)
|
||||
{
|
||||
for (const auto&j: FollowSet[key])
|
||||
{
|
||||
if (FollowSet[*i].count(j) == 0)
|
||||
{
|
||||
FollowSet[*i].emplace(j);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FollowSet[*i].count(c) == 0)
|
||||
{
|
||||
FollowSet[*i].emplace(c);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成预测分析表
|
||||
for (const auto&[key, array]: Generators)
|
||||
{
|
||||
for (const auto&expression: array)
|
||||
{
|
||||
const auto&firstSet = GetFirstSet(expression);
|
||||
|
||||
for (const auto&c: firstSet)
|
||||
{
|
||||
if (c == EMPTY)
|
||||
{
|
||||
for (const auto&i: FollowSet.at(key))
|
||||
{
|
||||
if (!AnalysisTable[key].emplace(i, expression).second)
|
||||
{
|
||||
std::cout << "Error, not LL(1) grammar!" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AnalysisTable[key].emplace(c, expression).second)
|
||||
{
|
||||
std::cout << "Error, not LL(1) grammar!" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<wchar_t> GetFirstSet(const std::wstring&expression) const
|
||||
{
|
||||
std::unordered_set<wchar_t> set;
|
||||
|
||||
if (Nonterminators.count(expression.front()) != 0)
|
||||
{
|
||||
// 起手是非终结符
|
||||
for (const auto&c: FirstSet.at(expression.front()))
|
||||
{
|
||||
// 只有所有的符号都能推出空串 才添加
|
||||
if (c == EMPTY)
|
||||
{
|
||||
bool flag = true;
|
||||
for (const auto&i: expression)
|
||||
{
|
||||
if (Nonterminators.count(i) != 0)
|
||||
{
|
||||
flag = FirstSet.at(i).count(EMPTY) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
flag = i == EMPTY;
|
||||
}
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
set.emplace(EMPTY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
set.emplace(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
set.emplace(expression.front());
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
void Analyse(const std::wstring& input, const std::unordered_map<std::wstring, int>& numbers)
|
||||
{
|
||||
std::list<wchar_t> states;
|
||||
auto i = input.begin();
|
||||
|
||||
states.emplace_back(END);
|
||||
states.emplace_back(Begin);
|
||||
|
||||
do
|
||||
{
|
||||
for (const auto c : states)
|
||||
{
|
||||
std::wcout << c;
|
||||
}
|
||||
std::wcout << L'\t';
|
||||
|
||||
for (auto j = i; j != input.end(); ++j)
|
||||
{
|
||||
std::wcout << *j;
|
||||
}
|
||||
std::wcout << L'\t';
|
||||
|
||||
if (auto top = states.back(); Nonterminators.count(top) != 0)
|
||||
{
|
||||
// 栈顶是非终结符
|
||||
if (AnalysisTable[top].count(*i) == 0)
|
||||
{
|
||||
std::wcout << L"error" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
states.pop_back();
|
||||
if (AnalysisTable[top][*i].front() != EMPTY)
|
||||
{
|
||||
// 如果是空串还是不要加了
|
||||
for (auto j = AnalysisTable[top][*i].rbegin(); j != AnalysisTable[top][*i].rend(); ++j)
|
||||
{
|
||||
states.emplace_back(*j);
|
||||
}
|
||||
}
|
||||
|
||||
auto expression = top + AnalysisTable[top][*i];
|
||||
std::wcout << numbers.at(expression);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (top == *i)
|
||||
{
|
||||
if (top == END)
|
||||
{
|
||||
std::wcout << L"accept";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wcout << L"match";
|
||||
}
|
||||
|
||||
states.pop_back();
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wcout << L"error" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::wcout << std::endl;
|
||||
}
|
||||
while (!states.empty());
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::unordered_map<wchar_t, std::vector<std::wstring>> map = {
|
||||
{'E', {L"TA"}},
|
||||
{'A', {L"+TA", L"-TA", L"ε"}},
|
||||
{'T', {L"FB"}},
|
||||
{'B', {L"ε", L"*FB", L"/FB"}},
|
||||
{'F', {L"(E)", L"n"}}
|
||||
};
|
||||
|
||||
// 通过另外的表输入产生式编号
|
||||
const std::unordered_map<std::wstring, int> numbers = {
|
||||
{L"ETA", 1},
|
||||
{L"A+TA", 2},
|
||||
{L"A-TA", 3},
|
||||
{L"Aε", 4},
|
||||
{L"TFB", 5},
|
||||
{L"B*FB", 6},
|
||||
{L"B/FB", 7},
|
||||
{L"Bε", 8},
|
||||
{L"F(E)", 9},
|
||||
{L"Fn", 10}
|
||||
};
|
||||
|
||||
/**
|
||||
FIRST
|
||||
E -> n (
|
||||
A -> e - +
|
||||
T -> ( n
|
||||
B -> / * e
|
||||
F -> n (
|
||||
FOLLOW
|
||||
E -> ) $
|
||||
A -> $ )
|
||||
T -> $ ) - +
|
||||
B -> + - ) $
|
||||
F -> + - ) $ / *
|
||||
*/
|
||||
Grammar grammar(map, 'E');
|
||||
|
||||
std::wstring input;
|
||||
std::wcin >> input;
|
||||
|
||||
input += END;
|
||||
|
||||
grammar.Analyse(input, numbers);
|
||||
return 0;
|
||||
}
|
443
GrammarParser/LrParser.cpp
Normal file
443
GrammarParser/LrParser.cpp
Normal file
@@ -0,0 +1,443 @@
|
||||
// ReSharper disable CppTooWideScopeInitStatement
|
||||
// ReSharper disable CppUseStructuredBinding
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#define END L'$'
|
||||
|
||||
struct Expression
|
||||
{
|
||||
char Left;
|
||||
char LookAhead;
|
||||
std::string Right;
|
||||
int Pos;
|
||||
|
||||
Expression(const char left, const std::string& right, const char lookAhead)
|
||||
{
|
||||
Left = left;
|
||||
LookAhead = lookAhead;
|
||||
Right = std::string(right);
|
||||
Pos = 0;
|
||||
}
|
||||
|
||||
std::string GetHashCode() const
|
||||
{
|
||||
std::stringstream hash;
|
||||
|
||||
hash << Left << Right << Pos << LookAhead;
|
||||
|
||||
return hash.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct ExpressionHash
|
||||
{
|
||||
std::size_t operator() (const Expression & expression) const
|
||||
{
|
||||
return std::hash<std::string>()(expression.GetHashCode());
|
||||
}
|
||||
};
|
||||
|
||||
struct ExpressionEqual
|
||||
{
|
||||
bool operator() (const Expression& a, const Expression& b) const
|
||||
{
|
||||
return a.GetHashCode() == b.GetHashCode();
|
||||
}
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
std::unordered_set<Expression, ExpressionHash, ExpressionEqual> Expressions;
|
||||
std::unordered_map<char, std::shared_ptr<State>> Transformers;
|
||||
|
||||
explicit State(const std::unordered_set<Expression, ExpressionHash, ExpressionEqual>& expressions)
|
||||
{
|
||||
Expressions = expressions;
|
||||
}
|
||||
|
||||
std::string GetHashCode() const
|
||||
{
|
||||
std::unordered_map<std::string, std::vector<char>> map;
|
||||
|
||||
for (const auto &e : Expressions)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << e.Left;
|
||||
|
||||
for (size_t i =0 ; i < e.Pos; ++i)
|
||||
{
|
||||
stream << e.Right[i];
|
||||
}
|
||||
stream << '^';
|
||||
for (size_t i = e.Pos; i < e.Right.size(); ++i)
|
||||
{
|
||||
stream << e.Right[i];
|
||||
}
|
||||
|
||||
const auto expression = stream.str();
|
||||
if (map.count(expression) == 0)
|
||||
{
|
||||
map[expression] = std::vector<char>();
|
||||
map[expression].emplace_back(e.LookAhead);
|
||||
}
|
||||
else
|
||||
{
|
||||
map[expression].emplace_back(e.LookAhead);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> list;
|
||||
for (auto& pair: map)
|
||||
{
|
||||
std::string hash(pair.first);
|
||||
std::sort(pair.second.begin(), pair.second.end());
|
||||
|
||||
for (const auto c : pair.second)
|
||||
{
|
||||
hash += c;
|
||||
}
|
||||
|
||||
list.emplace_back(hash);
|
||||
}
|
||||
|
||||
std::sort(list.begin(), list.end());
|
||||
|
||||
std::string hash;
|
||||
|
||||
for (const auto& s : list)
|
||||
{
|
||||
hash += s;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct StateHash
|
||||
{
|
||||
std::size_t operator() (const std::shared_ptr<State>& s) const
|
||||
{
|
||||
return std::hash<std::string>()(s->GetHashCode());
|
||||
}
|
||||
};
|
||||
|
||||
struct StateEqual
|
||||
{
|
||||
bool operator() (const std::shared_ptr<State>& a, const std::shared_ptr<State>& b) const
|
||||
{
|
||||
return a->GetHashCode() == b->GetHashCode();
|
||||
}
|
||||
};
|
||||
|
||||
struct Grammar
|
||||
{
|
||||
std::unordered_map<char, std::vector<std::string>> Generators;
|
||||
char Begin;
|
||||
std::shared_ptr<State> BeginState;
|
||||
|
||||
std::unordered_set<char> Nonterminators;
|
||||
std::unordered_map<char, std::unordered_set<char>> FirstSet;
|
||||
std::unordered_set<std::shared_ptr<State>, StateHash, StateEqual> DFA;
|
||||
|
||||
Grammar(const std::unordered_map<char, std::vector<std::string>>& map, const char begin)
|
||||
{
|
||||
Generators = map;
|
||||
Begin = begin;
|
||||
|
||||
for (const auto& pair : Generators)
|
||||
{
|
||||
Nonterminators.emplace(pair.first);
|
||||
}
|
||||
|
||||
// 构造FirstSet
|
||||
bool changed = true;
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
|
||||
for (const auto& pair: Generators)
|
||||
{
|
||||
for (const auto&expression: pair.second)
|
||||
{
|
||||
if (Nonterminators.count(expression.front()) != 0)
|
||||
{
|
||||
// 合并其他非终结符的FIRST集合
|
||||
for (const auto&c: FirstSet[expression.front()])
|
||||
{
|
||||
if (FirstSet[pair.first].count(c) == 0)
|
||||
{
|
||||
FirstSet[pair.first].emplace(c);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FirstSet[pair.first].count(expression.front()) == 0)
|
||||
{
|
||||
FirstSet[pair.first].emplace(expression.front());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Grammar()
|
||||
{
|
||||
DFA.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief 计算句子的First集合
|
||||
* \param expression 需要计算的句子
|
||||
* \return 句子的First集合
|
||||
*/
|
||||
std::unordered_set<char> GetFirstSet(const std::string& expression) const
|
||||
{
|
||||
std::unordered_set<char> result;
|
||||
|
||||
if (Nonterminators.count(expression.front()) != 0)
|
||||
{
|
||||
// 起手是非终结符
|
||||
for (const char c : FirstSet.at(expression.front()))
|
||||
{
|
||||
result.emplace(c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace(expression.front());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unordered_set<Expression, ExpressionHash, ExpressionEqual> ConstructClosure(const Expression& expression) const
|
||||
{
|
||||
std::unordered_set<Expression, ExpressionHash, ExpressionEqual> result;
|
||||
result.emplace(expression);
|
||||
|
||||
bool changed = true;
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
|
||||
for (const auto& e : result)
|
||||
{
|
||||
const char next = e.Right[e.Pos];
|
||||
|
||||
if (Nonterminators.count(next) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string ahead;
|
||||
|
||||
for (size_t i = e.Pos + 1; i < e.Right.size(); ++i)
|
||||
{
|
||||
ahead += e.Right[i];
|
||||
}
|
||||
ahead += e.LookAhead;
|
||||
|
||||
std::unordered_set<char> lookAheadSet = GetFirstSet(ahead);
|
||||
|
||||
for (const auto& i : Generators.at(next))
|
||||
{
|
||||
for (const char lookAhead : lookAheadSet)
|
||||
{
|
||||
Expression newExpression(next, i, lookAhead);
|
||||
if (result.count(newExpression) == 0)
|
||||
{
|
||||
result.emplace(newExpression);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ConstructDFA()
|
||||
{
|
||||
const Expression begin = Expression(Begin, Generators.at(Begin).front(), END);
|
||||
int id = 0;
|
||||
|
||||
BeginState = std::make_shared<State>(ConstructClosure(begin));
|
||||
DFA.emplace(BeginState);
|
||||
++id;
|
||||
|
||||
bool added = true;
|
||||
while (added)
|
||||
{
|
||||
added = false;
|
||||
|
||||
for (const auto& state : DFA)
|
||||
{
|
||||
// 表示使用key 进行移进可以生成的新LR(1)句型
|
||||
std::unordered_map<char, std::vector<Expression>> nextExpressions;
|
||||
|
||||
for (const auto& e : state->Expressions)
|
||||
{
|
||||
Expression nextExpression = Expression(e);
|
||||
if (nextExpression.Pos >= nextExpression.Right.size())
|
||||
{
|
||||
// 移进符号已经到达句型的末尾
|
||||
continue;
|
||||
}
|
||||
nextExpression.Pos++;
|
||||
|
||||
if (nextExpressions.count(e.Right[e.Pos]) == 0)
|
||||
{
|
||||
std::vector<Expression> list;
|
||||
list.emplace_back(nextExpression);
|
||||
nextExpressions.emplace(e.Right[e.Pos], list);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextExpressions.at(e.Right[e.Pos]).emplace_back(nextExpression);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& pair : nextExpressions)
|
||||
{
|
||||
// 针对每个构建项目集闭包
|
||||
std::unordered_set<Expression, ExpressionHash, ExpressionEqual> closure;
|
||||
|
||||
for (const auto& i : pair.second)
|
||||
{
|
||||
for (const auto& e : ConstructClosure(i))
|
||||
{
|
||||
closure.emplace(e);
|
||||
}
|
||||
}
|
||||
|
||||
auto nextState = std::make_shared<State>(closure);
|
||||
|
||||
auto iter = DFA.find(nextState);
|
||||
if (iter == DFA.end())
|
||||
{
|
||||
// 不存在这个项目集闭包
|
||||
DFA.emplace(nextState);
|
||||
state->Transformers.emplace(pair.first, nextState);
|
||||
|
||||
++id;
|
||||
added = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 存在这个项目集闭包
|
||||
state->Transformers.emplace(pair.first, *iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Analyse(const std::unordered_map<std::string, int>& numbers, const std::string& input) const
|
||||
{
|
||||
std::list<std::shared_ptr<State>> stateStack;
|
||||
stateStack.emplace_back(BeginState);
|
||||
|
||||
std::string buffer(input);
|
||||
buffer += END;
|
||||
auto iter = buffer.begin();
|
||||
|
||||
while (true)
|
||||
{
|
||||
const auto& top = stateStack.back();
|
||||
|
||||
// 尝试进行移进
|
||||
bool acceptFlag = false;
|
||||
bool reduceFlag = false;
|
||||
for (const auto& expression : top->Expressions)
|
||||
{
|
||||
if (expression.Pos == expression.Right.size() and expression.LookAhead == *iter)
|
||||
{
|
||||
if (expression.Left == Begin)
|
||||
{
|
||||
acceptFlag = true;
|
||||
std::cout << "accept" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
reduceFlag = true;
|
||||
std::cout << numbers.at(expression.Left + expression.Right) << std::endl;
|
||||
|
||||
for (size_t i = 0; i < expression.Right.size(); ++i)
|
||||
{
|
||||
stateStack.pop_back();
|
||||
}
|
||||
|
||||
stateStack.emplace_back(stateStack.back()->Transformers.at(expression.Left));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (acceptFlag)
|
||||
{
|
||||
// 接受
|
||||
break;
|
||||
}
|
||||
if (reduceFlag)
|
||||
{
|
||||
// reduce
|
||||
continue;
|
||||
}
|
||||
|
||||
if (top->Transformers.count(*iter) != 0)
|
||||
{
|
||||
stateStack.emplace_back(top->Transformers.at(*iter));
|
||||
++iter;
|
||||
|
||||
std::cout << "shift" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "error" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::unordered_map<char, std::vector<std::string>> map = {
|
||||
{'S', {"E"}},
|
||||
{'E', {"E+T", "E-T", "T"}},
|
||||
{'T', {"T*F", "T/F", "F"}},
|
||||
{'F', {"(E)", "n"}}
|
||||
};
|
||||
|
||||
const std::unordered_map<std::string, int> numbers = {
|
||||
{"SE", 0},
|
||||
{"EE+T", 1},
|
||||
{"EE-T", 2},
|
||||
{"ET", 3},
|
||||
{"TT*F", 4},
|
||||
{"TT/F", 5},
|
||||
{"TF", 6},
|
||||
{"F(E)", 7},
|
||||
{"Fn", 8}
|
||||
};
|
||||
|
||||
Grammar grammar(map, 'S');
|
||||
grammar.ConstructDFA();
|
||||
|
||||
std::string input;
|
||||
std::cin >> input;
|
||||
|
||||
grammar.Analyse(numbers, input);
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user