-
Notifications
You must be signed in to change notification settings - Fork 0
/
TransitionList.h
136 lines (125 loc) · 3.59 KB
/
TransitionList.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#pragma once
#include "Terminal.h"
#include "TransitionAction.h"
#include "ConflictResolver.h"
#include "BinaryReader.h"
#include "BinaryWriter.h"
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <iterator>
#include <stdexcept>
class TransitionList
{
public:
TransitionList() = default;
TransitionList(std::size_t index)
: index(index)
{
}
void AddAction(const Terminal& terminal, const TransitionAction& action, ConflictResolver& resolver)
{
auto iter = actions.find(terminal);
if (iter != actions.end())
{
if (iter->second != action)
{
switch (resolver.Resolve(terminal, actions[terminal], action))
{
case ConflictResolution::KeepNew:
actions[terminal] = action;
break;
case ConflictResolution::KeepOld:
//nothing, keeping old action and throwing away new action
break;
case ConflictResolution::None:
throw std::runtime_error("Conflicting actions generated.");
}
}
}
else
actions[terminal] = action;
}
void AddGoto(const std::string& nonTerminal, std::size_t state)
{
auto iter = gotos.find(nonTerminal);
if (iter == gotos.end())
gotos[nonTerminal] = state;
else if (iter->second != state)
throw std::runtime_error("Conflicting gotos generated.");
}
TransitionAction GetAction(const Token& token)
{
std::vector<std::pair<Terminal, TransitionAction>> matchingActions;
std::copy_if(actions.begin(), actions.end(), std::back_inserter(matchingActions), [&](auto& p){ return p.first == token; });
if (matchingActions.empty())
throw std::runtime_error("Error: missing action for terminal: " + token.ToString());
if (matchingActions.size() == 1)
return matchingActions.front().second;
return ResolveMultipleTransitions(token, matchingActions);
}
std::size_t GetGoto(const std::string& nonTerminal)
{
auto iter = gotos.find(nonTerminal);
if (iter == gotos.end())
throw std::runtime_error("Error: missing goto for non-terminal.");
return iter->second;
}
void Write(BinaryWriter& writer) const
{
writer.Write(index);
writer.Write(actions.size());
for (auto& item : actions)
{
item.first.Write(writer);
item.second.Write(writer);
}
writer.Write(gotos.size());
for (auto& item : gotos)
{
writer.Write(item.first);
writer.Write(item.second);
}
}
void Read(BinaryReader& reader)
{
index = reader.ReadSize();
auto actionCount = reader.ReadSize();
for (auto index = 0u; index < actionCount; ++index)
{
Terminal terminal;
terminal.Read(reader);
TransitionAction action;
action.Read(reader);
actions.insert({ terminal, action });
}
auto gotoCount = reader.ReadSize();
for (auto index = 0u; index < gotoCount; ++index)
{
auto nonTerminal = reader.ReadString();
auto state = reader.ReadSize();
gotos.insert({ nonTerminal, state });
}
}
private:
TransitionAction ResolveMultipleTransitions(const Token& token, const std::vector<std::pair<Terminal, TransitionAction>>& matchingActions)
{
//Multiple matching identifiers: id vs. id.value, choose id.value
if (matchingActions.front().first.GetType() == TokenType::Identifier && matchingActions.size() == 2)
{
auto firstIsId = matchingActions.front().first.GetValue().empty();
auto secondIsId = matchingActions.back().first.GetValue().empty();
if (firstIsId != secondIsId)
return firstIsId ?
matchingActions.back().second :
matchingActions.front().second;
}
throw std::runtime_error("Multiple actions found for token");
}
private:
std::size_t index = 0;
std::map<Terminal, TransitionAction> actions;
std::map<std::string, std::size_t> gotos;
};