Skip to content

Commit d7d4049

Browse files
authored
fix parser-generator error occurring when state closure overrapped
1 parent b0ef1f1 commit d7d4049

File tree

1 file changed

+138
-20
lines changed

1 file changed

+138
-20
lines changed

ParserGenerator.cs

Lines changed: 138 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,88 @@
1313

1414
namespace ParserGenerator
1515
{
16+
public class ParserAction
17+
{
18+
public Action<ParsingTree.ParsingTreeNode> SemanticAction;
19+
public static ParserAction Create(Action<ParsingTree.ParsingTreeNode> action)
20+
=> new ParserAction { SemanticAction = action };
21+
}
22+
1623
public class ParserProduction
1724
{
1825
public int index;
1926
public string production_name;
2027
public bool isterminal;
2128
public List<ParserProduction> contents = new List<ParserProduction>();
2229
public List<List<ParserProduction>> sub_productions = new List<List<ParserProduction>>();
30+
public List<ParserAction> temp_actions = new List<ParserAction>();
31+
public List<ParserAction> actions = new List<ParserAction>();
2332

2433
public static ParserProduction operator +(ParserProduction p1, ParserProduction p2)
2534
{
2635
p1.contents.Add(p2);
2736
return p1;
2837
}
2938

39+
public static ParserProduction operator +(ParserProduction pp, ParserAction ac)
40+
{
41+
pp.temp_actions.Add(ac);
42+
return pp;
43+
}
44+
3045
public static ParserProduction operator |(ParserProduction p1, ParserProduction p2)
3146
{
3247
p2.contents.Insert(0, p2);
3348
p1.sub_productions.Add(new List<ParserProduction>(p2.contents));
49+
p1.actions.AddRange(p2.temp_actions);
50+
p2.temp_actions.Clear();
51+
p2.contents.Clear();
52+
return p1;
53+
}
54+
55+
#if false
56+
public static ParserProduction operator +(ParserProduction p1, string p2)
57+
{
58+
p1.contents.Add(new ParserProduction { isterminal = true, token_specific = p2 });
59+
return p1;
60+
}
61+
62+
public static ParserProduction operator|(ParserProduction p1, string p2)
63+
{
64+
p1.sub_productions.Add(new List<ParserProduction> { p1, new ParserProduction { isterminal = true, token_specific = p2 } });
65+
return p1;
66+
}
67+
#endif
68+
}
69+
70+
public class ParserProduction
71+
{
72+
public int index;
73+
public string production_name;
74+
public bool isterminal;
75+
public List<ParserProduction> contents = new List<ParserProduction>();
76+
public List<List<ParserProduction>> sub_productions = new List<List<ParserProduction>>();
77+
public List<ParserAction> temp_actions = new List<ParserAction>();
78+
public List<ParserAction> actions = new List<ParserAction>();
79+
80+
public static ParserProduction operator +(ParserProduction p1, ParserProduction p2)
81+
{
82+
p1.contents.Add(p2);
83+
return p1;
84+
}
85+
86+
public static ParserProduction operator +(ParserProduction pp, ParserAction ac)
87+
{
88+
pp.temp_actions.Add(ac);
89+
return pp;
90+
}
91+
92+
public static ParserProduction operator |(ParserProduction p1, ParserProduction p2)
93+
{
94+
p2.contents.Insert(0, p2);
95+
p1.sub_productions.Add(new List<ParserProduction>(p2.contents));
96+
p1.actions.AddRange(p2.temp_actions);
97+
p2.temp_actions.Clear();
3498
p2.contents.Clear();
3599
return p1;
36100
}
@@ -86,13 +150,23 @@ public void PushStarts(ParserProduction pp)
86150
production_rules[0].sub_productions.Add(new List<ParserProduction> { pp });
87151
}
88152

153+
/// <summary>
154+
/// 터미널들의 Shift-Reduce Conflict solve 정보를 넣습니다.
155+
/// </summary>
156+
/// <param name="left"></param>
157+
/// <param name="terminals"></param>
89158
public void PushConflictSolver(bool left, params ParserProduction[] terminals)
90159
{
91160
var priority = shift_reduce_conflict_solve.Count + shift_reduce_conflict_solve_with_production_rule.Count;
92161
foreach (var pp in terminals)
93162
shift_reduce_conflict_solve.Add(pp.index, new Tuple<int, bool>(priority, left));
94163
}
95164

165+
/// <summary>
166+
/// 논터미널들의 Shift-Reduce Conflict solve 정보를 넣습니다.
167+
/// </summary>
168+
/// <param name="left"></param>
169+
/// <param name="no"></param>
96170
public void PushConflictSolver(bool left, params Tuple<ParserProduction, int>[] no)
97171
{
98172
var priority = shift_reduce_conflict_solve.Count + shift_reduce_conflict_solve_with_production_rule.Count;
@@ -103,8 +177,11 @@ public void PushConflictSolver(bool left, params Tuple<ParserProduction, int>[]
103177
shift_reduce_conflict_solve_with_production_rule[ppi.Item1.index].Add(ppi.Item2, new Tuple<int, bool>(priority, left));
104178
}
105179
}
106-
180+
107181
#region String Hash Function
182+
// 원래 해시가 아니라 set로 구현해야하는게 일반적임
183+
// 집합끼리의 비교연산, 일치여부 교집합을 구해 좀 더 최적화가능하지만 귀찮으니 string-hash를 쓰도록한다.
184+
108185
private string t2s(Tuple<int, int, int> t)
109186
{
110187
return $"{t.Item1},{t.Item2},{t.Item3}";
@@ -754,6 +831,7 @@ public void GenerateLALR()
754831
set.Add(t2s(psd));
755832
// Find all transitions
756833
var new_trans = new List<Tuple<int, int, int, HashSet<int>>>();
834+
var trans_dic = new Dictionary<string, int>();
757835
foreach (var psd in goto_unit.Value)
758836
{
759837
if (production_rules[psd.Item1].sub_productions[psd.Item2].Count == psd.Item3) continue;
@@ -762,8 +840,17 @@ public void GenerateLALR()
762840
foreach (var nts in first_nt)
763841
if (!set.Contains(t2s(nts)))
764842
{
765-
new_trans.Add(nts);
766-
set.Add(t2s(nts));
843+
var ts = t2s(new Tuple<int, int, int>(nts.Item1, nts.Item2, nts.Item3));
844+
if (trans_dic.ContainsKey(ts))
845+
{
846+
nts.Item4.ToList().ForEach(x => new_trans[trans_dic[ts]].Item4.Add(x));
847+
}
848+
else
849+
{
850+
trans_dic.Add(ts, new_trans.Count);
851+
new_trans.Add(nts);
852+
set.Add(t2s(nts));
853+
}
767854
}
768855
}
769856
goto_unit.Value.AddRange(new_trans);
@@ -773,14 +860,30 @@ public void GenerateLALR()
773860
var index_list = new List<Tuple<int, int>>();
774861
foreach (var pp in gotos)
775862
{
776-
var hash = l2s(pp.Value);
777-
if (!state_index.ContainsKey(hash))
863+
try
778864
{
779-
states.Add(index_count, pp.Value);
780-
state_index.Add(hash, index_count);
781-
q.Enqueue(index_count++);
865+
var hash = l2s(pp.Value);
866+
if (!state_index.ContainsKey(hash))
867+
{
868+
states.Add(index_count, pp.Value);
869+
state_index.Add(hash, index_count);
870+
q.Enqueue(index_count++);
871+
}
872+
index_list.Add(new Tuple<int, int>(pp.Key, state_index[hash]));
873+
}
874+
catch
875+
{
876+
// Now this error is not hit
877+
// For debugging
878+
print_header("GOTO CONFLICT!!");
879+
GlobalPrinter.Append($"Cannot solve lookahead overlapping!\r\n");
880+
GlobalPrinter.Append($"Please uses non-associative option or adds extra token to handle with shift-reduce conflict!\r\n");
881+
print_states(p, states[p]);
882+
print_header("INCOMPLETE STATES");
883+
foreach (var s in states)
884+
print_states(s.Key, s.Value);
885+
return;
782886
}
783-
index_list.Add(new Tuple<int, int>(pp.Key, state_index[hash]));
784887
}
785888

786889
goto_table.Add(new Tuple<int, List<Tuple<int, int>>>(p, index_list));
@@ -887,15 +990,17 @@ public void GenerateLALR()
887990
GlobalPrinter.Append($"Shift-Reduce Conflict! {(tuple.Item1 == -1 ? "$" : production_rules[tuple.Item1].production_name)}\r\n");
888991
GlobalPrinter.Append($"States: {ms.Key} {tuple.Item2}\r\n");
889992
print_states(ms.Key, states[ms.Key]);
890-
print_states(shift_tokens[tuple.Item1], states[shift_tokens[tuple.Item1]]);
993+
print_states(small_shift_info[shift_tokens[tuple.Item1]].Item2, states[small_shift_info[shift_tokens[tuple.Item1]].Item2]);
891994
#endif
892995
var pp = get_first_on_right_terminal(production_rules[tuple.Item2], tuple.Item3);
893996

894-
if (!shift_reduce_conflict_solve.ContainsKey(pp.index) || !shift_reduce_conflict_solve.ContainsKey(tuple.Item1))
895-
throw new Exception($"Specify the rules to resolve Shift-Reduce Conflict! Target: {production_rules[tuple.Item1].production_name} {pp.production_name}");
896-
var p1 = shift_reduce_conflict_solve[pp.index];
897-
var p2 = shift_reduce_conflict_solve[tuple.Item1];
898-
997+
Tuple<int, bool> p1 = null, p2 = null;
998+
999+
if (shift_reduce_conflict_solve.ContainsKey(pp.index))
1000+
p1 = shift_reduce_conflict_solve[pp.index];
1001+
if (shift_reduce_conflict_solve.ContainsKey(tuple.Item1))
1002+
p2 = shift_reduce_conflict_solve[tuple.Item1];
1003+
8991004
if (shift_reduce_conflict_solve_with_production_rule.ContainsKey(tuple.Item2))
9001005
if (shift_reduce_conflict_solve_with_production_rule[tuple.Item2].ContainsKey(tuple.Item3))
9011006
p1 = shift_reduce_conflict_solve_with_production_rule[tuple.Item2][tuple.Item3];
@@ -904,6 +1009,9 @@ public void GenerateLALR()
9041009
if (shift_reduce_conflict_solve_with_production_rule[states[tuple.Item1][0].Item1].ContainsKey(states[tuple.Item1][0].Item2))
9051010
p2 = shift_reduce_conflict_solve_with_production_rule[states[tuple.Item1][0].Item1][states[tuple.Item1][0].Item2];
9061011

1012+
if (p1 == null || p2 == null)
1013+
throw new Exception($"Specify the rules to resolve Shift-Reduce Conflict! Target: {production_rules[tuple.Item1].production_name} {pp.production_name}");
1014+
9071015
if (p1.Item1 < p2.Item1 || (p1.Item1 == p2.Item1 && p1.Item2))
9081016
{
9091017
// Reduce
@@ -947,6 +1055,9 @@ public void GenerateLALR()
9471055
}
9481056
#endregion
9491057

1058+
/// <summary>
1059+
/// 파싱 테이블을 집합형태로 출력합니다.
1060+
/// </summary>
9501061
public void PrintStates()
9511062
{
9521063
print_header("FINAL STATES");
@@ -967,6 +1078,9 @@ public void PrintStates()
9671078
}
9681079
}
9691080

1081+
/// <summary>
1082+
/// 파싱테이블을 테이블 형태로 출력합니다.
1083+
/// </summary>
9701084
public void PrintTable()
9711085
{
9721086
var production_mapping = new List<List<int>>();
@@ -1266,6 +1380,7 @@ public ShiftReduceParser CreateShiftReduceParserInstance()
12661380
var grammar = new List<List<int>>();
12671381
var grammar_group = new List<int>();
12681382
var production_mapping = new List<List<int>>();
1383+
var semantic_rules = new List<ParserAction>();
12691384
var pm_count = 0;
12701385

12711386
foreach (var pr in production_rules)
@@ -1316,10 +1431,10 @@ public ShiftReduceParser CreateShiftReduceParserInstance()
13161431
}
13171432
}
13181433

1319-
return new ShiftReduceParser(symbol_table, jump_table, goto_table, grammar_group.ToArray(), grammar.Select(x => x.ToArray()).ToArray());
1434+
return new ShiftReduceParser(symbol_table, jump_table, goto_table, grammar_group.ToArray(), grammar.Select(x => x.ToArray()).ToArray(), semantic_rules);
13201435
}
13211436
}
1322-
1437+
13231438
public class ParsingTree
13241439
{
13251440
public class ParsingTreeNode
@@ -1346,7 +1461,7 @@ public ParsingTree(ParsingTreeNode root)
13461461
this.root = root;
13471462
}
13481463
}
1349-
1464+
13501465
/// <summary>
13511466
/// Shift-Reduce Parser for LR(1)
13521467
/// </summary>
@@ -1356,6 +1471,7 @@ public class ShiftReduceParser
13561471
List<string> symbol_index_name = new List<string>();
13571472
Stack<int> state_stack = new Stack<int>();
13581473
Stack<ParsingTree.ParsingTreeNode> treenode_stack = new Stack<ParsingTree.ParsingTreeNode>();
1474+
List<ParserAction> actions;
13591475

13601476
// 3 1 2 0
13611477
// Accept? Shift? Reduce? Error?
@@ -1364,13 +1480,14 @@ public class ShiftReduceParser
13641480
int[][] production;
13651481
int[] group_table;
13661482

1367-
public ShiftReduceParser(Dictionary<string, int> symbol_table, int[][] jump_table, int[][] goto_table, int[] group_table, int[][] production)
1483+
public ShiftReduceParser(Dictionary<string, int> symbol_table, int[][] jump_table, int[][] goto_table, int[] group_table, int[][] production, List<ParserAction> actions)
13681484
{
13691485
symbol_name_index = symbol_table;
13701486
this.jump_table = jump_table;
13711487
this.goto_table = goto_table;
13721488
this.production = production;
13731489
this.group_table = group_table;
1490+
this.actions = actions;
13741491
var l = symbol_table.ToList().Select(x => new Tuple<int, string>(x.Value, x.Key)).ToList();
13751492
l.Sort();
13761493
l.ForEach(x => symbol_index_name.Add(x.Item2));
@@ -1451,6 +1568,7 @@ private void reduce(int index)
14511568
reduction_parent.Contents = string.Join("", reduce_treenodes.Select(x => x.Contents));
14521569
reduction_parent.Childs = reduce_treenodes;
14531570
treenode_stack.Push(reduction_parent);
1571+
actions[reduction_parent.ProductionRuleIndex].SemanticAction(reduction_parent);
14541572
}
14551573
}
1456-
}
1574+
}

0 commit comments

Comments
 (0)