-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathContexter.cs
More file actions
178 lines (146 loc) · 5.79 KB
/
Contexter.cs
File metadata and controls
178 lines (146 loc) · 5.79 KB
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
using SER.Code.ContextSystem.BaseContexts;
using SER.Code.ContextSystem.Contexts.Control;
using SER.Code.ContextSystem.Interfaces;
using SER.Code.Extensions;
using SER.Code.Helpers;
using SER.Code.Helpers.ResultSystem;
using SER.Code.ScriptSystem;
using SER.Code.TokenSystem.Structures;
using SER.Code.TokenSystem.Tokens;
namespace SER.Code.ContextSystem;
/// <summary>
/// Responsible for joining tokens from a line together into contexts for execution.
/// </summary>
public static class Contexter
{
public static TryGet<Context[]> ContextLines(Line[] lines, Script scr)
{
Stack<StatementContext> statementStack = [];
List<Context> contexts = [];
List<Result> errors = [];
foreach (var line in lines)
{
Result mainErr = $"Line {line.LineNumber} cannot compile.";
if (ContextLine(line.Tokens, line.LineNumber, scr)
.HasErrored(out var error, out var context))
{
errors.Add(mainErr + error);
continue;
}
if (context is null) continue;
if (TryAddResult(context, line.LineNumber, statementStack, contexts).HasErrored(out var addError))
{
errors.Add(mainErr + addError);
continue;
}
Log.Debug($"current statement stack: {statementStack.Select(s => s.GetType().Name).JoinStrings(" -> ")}");
}
if (errors.Any()) return Result.Merge(errors);
return contexts.ToArray();
}
private static Result TryAddResult(
Context context,
uint lineNum,
Stack<StatementContext> statementStack,
List<Context> contexts
) {
Result rs = $"Invalid context {context}";
Log.Debug($"Trying to add context {context}");
switch (context)
{
case EndKeyword:
{
if (statementStack.Count == 0)
return rs + "There is no valid statement to close with the 'end' keyword! Check if the statement you are trying to close hasn't thrown an error when compiling.".AsError();
statementStack.Pop();
return true;
}
case IRequirePreviousStatementContext rqsContext:
{
if (statementStack.Count == 0)
{
return rs + $"{context} expected to be inside a statement, but it isn't.";
}
if (rqsContext.AcceptStatement(statementStack.Peek()).HasErrored(out var asError))
{
return rs + asError;
}
break;
}
}
var currentStatement = statementStack.FirstOrDefault();
string error;
if (context is StatementContext treeExtenderContext and IStatementExtender treeExtenderInfo)
{
if (currentStatement is null)
{
return rs + "There is no statement to extend.";
}
if (currentStatement is not IExtendableStatement extendable)
{
return rs + "The statement to extend is not extendable.";
}
if (!extendable.AllowedSignals.HasFlag(treeExtenderInfo.Extends))
{
return rs + "The statement to extend does not support this type of extension.";
}
extendable.RegisteredSignals[treeExtenderInfo.Extends] = treeExtenderContext;
statementStack.Pop();
statementStack.Push(treeExtenderContext);
return context.VerifyCurrentState().HasErrored(out error) ? rs + error : true;
}
if (context.VerifyCurrentState().HasErrored(out error))
return rs + error;
if (currentStatement is not null)
{
Log.Debug($"Adding finished context {context} to tree context {currentStatement}");
currentStatement.Children.Add(context);
context.ParentContext = currentStatement;
}
else
{
Log.Debug($"Adding finished context {context} to main collection");
contexts.Add(context);
}
if (context is StatementContext treeContext)
statementStack.Push(treeContext);
Log.Debug($"Line {lineNum} has been contexted to {context}");
return true;
}
public static TryGet<Context?> ContextLine(BaseToken[] tokens, uint? lineNum, Script scr)
{
Result rs = $"Line {(lineNum.HasValue ? $"{lineNum.Value} " : "")}is invalid";
var firstToken = tokens.FirstOrDefault();
if (firstToken is null) return null as Context;
if (firstToken is not IContextableToken contextable)
{
return $"'{firstToken.RawRep}' is not a valid way to start a line. Perhaps you made a typo?";
}
var context = contextable.GetContext(scr);
foreach (var token in tokens.Skip(1))
{
if (HandleCurrentContext(token, context, out var endLineContexting).HasErrored(out var errorMsg))
return rs + errorMsg;
if (endLineContexting) break;
}
return context;
}
private static Result HandleCurrentContext(BaseToken token, Context context, out bool endLineContexting)
{
Result rs = $"Cannot add '{token.RawRep}' to {context}";
Log.Debug($"Handling token {token} in context {context}");
var result = context.TryAddToken(token);
if (result.HasErrored)
{
endLineContexting = true;
return rs + result.ErrorMessage;
}
if (result.ShouldContinueExecution)
{
endLineContexting = false;
return true;
}
endLineContexting = true;
return true;
}
}