-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLispParser.cs
More file actions
322 lines (305 loc) · 7 KB
/
LispParser.cs
File metadata and controls
322 lines (305 loc) · 7 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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
using System;
using System.Collections.Generic;
using System.Numerics;
namespace libQCLISP
{
public class LispParser
{
/// <summary>
/// Parser state containing the current parsing status of the code
/// </summary>
public class ParserState{
public string data;
public int position;
public List<ILispValue> root;
}
/// <summary>
/// Initializes a new instance of the <see cref="libQCLISP.LispParser"/> class.
/// </summary>
public LispParser ()
{
}
/// <summary>
/// Parse the specified code using a specified registry and bank.
/// </summary>
/// <param name="data">The code to parse</param>
/// <param name="registry">Registry of the context</param>
/// <param name="bank">Bank of the context</param>
public ILispValue parse(string data,Dictionary<string,ILispNative> registry,Dictionary<string,ILispValue> bank)
{
ParserState state = new ParserState();
state.position = 0;
state.data = data + " ";
state.root = new List<ILispValue>();
for(;state.position<state.data.Length;state.position++)
{
int sign = 1;
bool no_eval;
switch (state.data [state.position]) {
case '\'':
case '(':
if (state.data [state.position] == '\'') {
no_eval = true;
state.position++;
} else
no_eval = false;
var n = resolve_parenthesis (ref state);
string child = state.data.Substring (n.beg + 1, n.end - n.beg - 1);
LispArray tmp_insertee = (LispArray)parse (child, registry, bank);
tmp_insertee.force_eval = !no_eval;
state.root.Add(tmp_insertee);
state.position--;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
case '-':
if (
Char.IsDigit (state.data [state.position])
|| state.data [state.position] == '.'
|| (
Char.IsDigit (state.data [state.position + 1])
&& state.data [state.position] == '-'
)) {
if (state.data [state.position] == '-') {
sign = -1;
state.position++;
}
ILispValue symn = resolve_numeric (ref state, sign);
state.root.Add (symn);
state.position--;
} else {
goto default;
}
break;
case '"':
ILispValue symst = new LispString(resolve_string (ref state));
state.root.Add (symst);
break;
case ' ':
case '\t':
case '\n':
break;
default:
string syms = resolve_symbol (ref state);
if (syms != "") {
state.root.Add (registry [syms]);
state.position--;
}
break;
}
}
return new LispArray(state.root);
}
/// <summary>
/// Resolves the symbol as a string
/// </summary>
/// <returns>The symbol as a system string</returns>
/// <param name="state">State pointed on the first character of the symbol</param>
string resolve_symbol(ref ParserState state)
{
HashSet<char> lws = new HashSet<char>();
lws.Add ('\t');
lws.Add ('\n');
lws.Add ('\0');
lws.Add (' ');
lws.Add ('(');
lws.Add (')');
string ret = "";
while(!(lws.Contains(state.data[state.position])))
{
ret += state.data [state.position];
state.position++;
}
return ret;
}
/// <summary>
/// Resolves the string and all its escape sequences
/// </summary>
/// <returns>The string as system string</returns>
/// <param name="state">State pointing to an opening '"'</param>
string resolve_string(ref ParserState state)
{
string ret = "";
state.position++;
while(!(state.data[state.position]=='"'))
{
if ((state.data [state.position] == '\\')) {
state.position++;
switch (state.data [state.position]) {
case 'n':
ret += '\n';
break;
case 'a':
ret += '\a';
break;
case 't':
ret += '\t';
break;
case '\\':
ret += '\\';
break;
case '"':
ret += '"';
break;
case '\n':
ret += '\n';
break;
default:
ret += state.data [state.position];
break;
}
} else {
ret += state.data [state.position];
}
state.position++;
}
return ret;
}
/// <summary>
/// Integer range
/// </summary>
struct Range{
public int beg,end;
}
/// <summary>
/// Resolves the two parenthesis that match from the state
/// </summary>
/// <returns>The parenthesis range as a Range</returns>
/// <param name="state">State pointed to an open parenthesis</param>
Range resolve_parenthesis(ref ParserState state)
{
Range r;
r.beg = state.position;
int rec = 1;
while (rec != 0) {
state.position++;
if (state.data [state.position] == '(')
rec++;
else if (state.data [state.position] == ')')
rec--;
}
r.end = state.position;
return r;
}
/// <summary>
/// Resolves the next numeric in the parser state and update the Parser.
/// </summary>
/// <returns>The numeric as a LispValue</returns>
/// <param name="state">State from which parsing is started</param>
/// <param name="coef">Coef of multiplication, most of the time used to define the sign</param>
ILispValue resolve_numeric(ref ParserState state,double coef)
{
BigInteger ret = 0;
int exp=0;
bool isInt = true;
bool end = false;
while (!end) {
if (Char.IsDigit (state.data [state.position])) {
ret *= 10;
switch (state.data [state.position]) {
case '9':
ret += 9;
break;
case '8':
ret += 8;
break;
case '7':
ret += 7;
break;
case '6':
ret += 6;
break;
case '5':
ret += 5;
break;
case '4':
ret += 4;
break;
case '3':
ret += 3;
break;
case '2':
ret += 2;
break;
case '1':
ret += 1;
break;
case '0':
break;
}
if (!isInt)
exp++;
} else if (Char.IsLetter (state.data [state.position])) {
switch (state.data [state.position]) {
case 'f':
exp += 15;
isInt = false;
break;
case 'p':
exp += 12;
isInt = false;
break;
case 'n':
exp += 9;
isInt = false;
break;
case 'u':
exp += 6;
isInt = false;
break;
case 'm':
exp += 3;
isInt = false;
break;
case 'c':
exp += 2;
isInt = false;
break;
case 'd':
exp += 1;
isInt = false;
break;
case 'h':
ret *= 100;
break;
case 'k':
ret *= 1000;
break;
case 'M':
ret *= 1000000;
break;
case 'G':
ret *= 1000000000;
break;
case 'T':
ret *= 1000000000000;
break;
case 'P':
ret *= 1000000000000000;
break;
}
end = true;
} else if (state.data [state.position]=='\'') {
} else if (state.data [state.position]=='.') {
isInt = false;
} else {
end = true;
}
state.position++;
}
if (isInt)
return new LispInteger ((int)coef*ret*(new BigInteger(Math.Pow(10,exp))));
else
return new LispFloating (coef*(double)ret/Math.Pow(10,exp));
}
}
}