pure-dango 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/package.json +45 -0
- package/src/core/compiler.ts +1269 -0
- package/src/core/interpreter.ts +2042 -0
- package/src/core/parser/handlers.ts +1091 -0
- package/src/core/parser/helpers.ts +157 -0
- package/src/core/parser/main.ts +620 -0
- package/src/core/tokenizer/funnies.ts +43 -0
- package/src/core/tokenizer/tokenizer.ts +334 -0
- package/src/core/utils.ts +534 -0
- package/src/index.ts +190 -0
- package/src/runtime/errors.ts +239 -0
- package/src/runtime/globals.ts +11 -0
- package/src/runtime/libs/ai.pds +56 -0
- package/src/runtime/libs/errors.pds +74 -0
- package/src/runtime/libs/gambling.pds +93 -0
- package/src/runtime/libs/io.pds +371 -0
- package/src/runtime/libs/math.pds +86 -0
- package/src/runtime/libs/std.pds +2 -0
- package/src/runtime/libs/types.pds +1232 -0
- package/src/runtime/stdlib.ts +1483 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
// the parser for pure-dango
|
|
2
|
+
// handles syntax rules
|
|
3
|
+
// turns the tokens from tokenizer in to objects the compiler can read
|
|
4
|
+
|
|
5
|
+
import {parseErrors} from "../../runtime/errors";
|
|
6
|
+
import {errorTemplate} from "../../runtime/stdlib";
|
|
7
|
+
import * as handlers from "./handlers";
|
|
8
|
+
import * as helpers from "./helpers";
|
|
9
|
+
|
|
10
|
+
// the operators of pure-dango
|
|
11
|
+
export const OPERATORS : Operators =
|
|
12
|
+
{
|
|
13
|
+
// operators
|
|
14
|
+
"+": {prec: 10, assoc: "left", type: "binary"},
|
|
15
|
+
"-": {prec: 10, assoc: "left", type: "binary"},
|
|
16
|
+
"*": {prec: 20, assoc: "left", type: "binary"},
|
|
17
|
+
"/": {prec: 20, assoc: "left", type: "binary"},
|
|
18
|
+
"%": {prec: 20, assoc: "left", type: "binary"},
|
|
19
|
+
|
|
20
|
+
// assignment operators
|
|
21
|
+
"=": {prec: 5, assoc: "right", type: "assignment"},
|
|
22
|
+
"-=": {prec: 5, assoc: "right", type: "assignment"},
|
|
23
|
+
"+=": {prec: 5, assoc: "right", type: "assignment"},
|
|
24
|
+
"*=": {prec: 5, assoc: "right", type: "assignment"},
|
|
25
|
+
"/=": {prec: 5, assoc: "right", type: "assignment"},
|
|
26
|
+
|
|
27
|
+
// equality operators
|
|
28
|
+
"==": {prec: 7, assoc: "left", type: "binary"},
|
|
29
|
+
"!=": {prec: 7, assoc: "left", type: "binary"},
|
|
30
|
+
">": {prec: 8, assoc: "left", type: "binary"},
|
|
31
|
+
"<": {prec: 8, assoc: "left", type: "binary"},
|
|
32
|
+
">=": {prec: 8, assoc: "left", type: "binary"},
|
|
33
|
+
"<=": {prec: 8, assoc: "left", type: "binary"},
|
|
34
|
+
|
|
35
|
+
// logical operators
|
|
36
|
+
"&&": {prec: 3, assoc: "left", type: "logical"},
|
|
37
|
+
"||": {prec: 2, assoc: "left", type: "logical"},
|
|
38
|
+
"??": {prec: 1, assoc: "left", type: "logical"},
|
|
39
|
+
|
|
40
|
+
// unary operators
|
|
41
|
+
"!": {prec: 30, assoc: "right", type: "unary", fix: "prefix"},
|
|
42
|
+
"~": {prec: 30, assoc: "right", type: "unary", fix: "prefix"},
|
|
43
|
+
"+u": {prec: 30, assoc: "right", type: "unary", fix: "prefix"},
|
|
44
|
+
"-u": {prec: 30, assoc: "right", type: "unary", fix: "prefix"},
|
|
45
|
+
"++": {prec: 30, assoc: "right", type: "unary", fix: "both"},
|
|
46
|
+
"--": {prec: 30, assoc: "right", type: "unary", fix: "both"},
|
|
47
|
+
|
|
48
|
+
// bitwise operators
|
|
49
|
+
"&": {prec: 9, assoc: "left", type: "binary"},
|
|
50
|
+
"^": {prec: 8, assoc: "left", type: "binary"},
|
|
51
|
+
"|": {prec: 7, assoc: "left", type: "binary"},
|
|
52
|
+
"<<": {prec: 12, assoc: "left", type: "binary"},
|
|
53
|
+
">>": {prec: 12, assoc: "left", type: "binary"},
|
|
54
|
+
">>>": {prec: 12, assoc: "left", type: "binary"},
|
|
55
|
+
"&=": {prec: 5, assoc: "right", type: "assignment"},
|
|
56
|
+
"|=": {prec: 5, assoc: "right", type: "assignment"},
|
|
57
|
+
"^=": {prec: 5, assoc: "right", type: "assignment"},
|
|
58
|
+
"<<=": {prec: 5, assoc: "right", type: "assignment"},
|
|
59
|
+
">>=": {prec: 5, assoc: "right", type: "assignment"},
|
|
60
|
+
">>>=": {prec: 5, assoc: "right", type: "assignment"},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function peek(tokens : Tokens, state : State) : BaseToken
|
|
64
|
+
{
|
|
65
|
+
return tokens[state.position];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function next(tokens : Tokens, state : State) : BaseToken | null
|
|
69
|
+
{
|
|
70
|
+
if (state.position >= tokens.length)
|
|
71
|
+
return null;
|
|
72
|
+
const token : BaseToken = tokens[state.position++];
|
|
73
|
+
state.lastToken = token;
|
|
74
|
+
return token;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isUnaryPrefix(operator : string) : boolean
|
|
78
|
+
{
|
|
79
|
+
return OPERATORS[operator]?.type === "unary" &&
|
|
80
|
+
OPERATORS[operator].fix !== "postfix";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseStartingToken(token : BaseToken, tokens : Tokens, state : State) : ParserToken | null
|
|
84
|
+
{
|
|
85
|
+
if (!token)
|
|
86
|
+
errorTemplate("parseStartingToken", `Unexpected end of input`);
|
|
87
|
+
|
|
88
|
+
let
|
|
89
|
+
{
|
|
90
|
+
type,
|
|
91
|
+
value,
|
|
92
|
+
row,
|
|
93
|
+
column
|
|
94
|
+
} = token;
|
|
95
|
+
|
|
96
|
+
if (type === "Keyword" && value === "function")
|
|
97
|
+
return handlers.parseFunctionNode(tokens, state, true);
|
|
98
|
+
|
|
99
|
+
if (type === "Keyword" && value === "inst")
|
|
100
|
+
return handlers.parseInstantationExpression(token, tokens, state);
|
|
101
|
+
|
|
102
|
+
if (type === "Operator" && (value === "+" || value === "-"))
|
|
103
|
+
{
|
|
104
|
+
const previousToken : BaseToken | null = state.lastToken;
|
|
105
|
+
|
|
106
|
+
const isUnaryContext : boolean =
|
|
107
|
+
!previousToken ||
|
|
108
|
+
previousToken.type === "Keyword" ||
|
|
109
|
+
previousToken.value === "(" ||
|
|
110
|
+
previousToken.value === "," ||
|
|
111
|
+
(
|
|
112
|
+
previousToken.type === "Operator" &&
|
|
113
|
+
previousToken.value !== ")" &&
|
|
114
|
+
previousToken.value !== "++" &&
|
|
115
|
+
previousToken.value !== "--"
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (isUnaryContext)
|
|
119
|
+
value += "u"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (type === "Literal" || type === "StringLiteral")
|
|
123
|
+
{
|
|
124
|
+
return {
|
|
125
|
+
type,
|
|
126
|
+
value,
|
|
127
|
+
row,
|
|
128
|
+
column
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isUnaryPrefix(value))
|
|
133
|
+
{
|
|
134
|
+
const valueObject = OPERATORS[value];
|
|
135
|
+
const operand = parseExpression(tokens, valueObject.prec, state)!;
|
|
136
|
+
|
|
137
|
+
if (valueObject.fix === "both")
|
|
138
|
+
{
|
|
139
|
+
return {
|
|
140
|
+
type: "UnaryExpression",
|
|
141
|
+
value: value.toString(),
|
|
142
|
+
argument: operand,
|
|
143
|
+
row,
|
|
144
|
+
column
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (valueObject.fix === "prefix")
|
|
149
|
+
{
|
|
150
|
+
return {
|
|
151
|
+
type: "UnaryExpression",
|
|
152
|
+
value,
|
|
153
|
+
argument: operand,
|
|
154
|
+
row,
|
|
155
|
+
column
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
throw new parseErrors.UnaryOperatorError(value, row, column);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (type === "Identifier")
|
|
163
|
+
{
|
|
164
|
+
const peekToken = peek(tokens, state);
|
|
165
|
+
|
|
166
|
+
// if the first item of tokens is "(" then treat it as a function
|
|
167
|
+
if (peekToken && peekToken.value === "(")
|
|
168
|
+
return parseFunctionCall(value, tokens, state);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
type: "VariableReference",
|
|
172
|
+
value,
|
|
173
|
+
row,
|
|
174
|
+
column
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (value === "(")
|
|
179
|
+
{
|
|
180
|
+
const expression = parseExpression(tokens, 0, state)!;
|
|
181
|
+
|
|
182
|
+
const peekToken = peek(tokens, state);
|
|
183
|
+
if (!peekToken || peekToken.value !== ")")
|
|
184
|
+
throw new parseErrors.MissingTokenError(")", expression.row, expression.column);
|
|
185
|
+
|
|
186
|
+
next(tokens, state); // eat )
|
|
187
|
+
|
|
188
|
+
return expression;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// handle ArrayAccesses
|
|
192
|
+
if (value === "[")
|
|
193
|
+
{
|
|
194
|
+
const elements : ParserToken[] = [];
|
|
195
|
+
|
|
196
|
+
while (peek(tokens, state) && peek(tokens, state).value !== "]")
|
|
197
|
+
{
|
|
198
|
+
const element = parseExpression(tokens, 0, state, true);
|
|
199
|
+
if (element)
|
|
200
|
+
elements.push(element);
|
|
201
|
+
|
|
202
|
+
const peekToken = peek(tokens, state);
|
|
203
|
+
if (peekToken && peekToken.value === ",")
|
|
204
|
+
next(tokens, state);
|
|
205
|
+
else if (peekToken && peekToken.value !== "]")
|
|
206
|
+
throw new parseErrors.UnexpectedTokenError(peekToken.value, peekToken.row, peekToken.column);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!peek(tokens, state) || peek(tokens, state).value !== "]")
|
|
210
|
+
throw new parseErrors.MissingTokenError("]", token.row, token.column);
|
|
211
|
+
|
|
212
|
+
next(tokens, state); // eat ]
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
type: "ArrayLiteral",
|
|
216
|
+
elements,
|
|
217
|
+
row,
|
|
218
|
+
column
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// handle Objects
|
|
223
|
+
if (value === "{")
|
|
224
|
+
{
|
|
225
|
+
const properties : {key : string, value : ParserToken | null}[] = [];
|
|
226
|
+
|
|
227
|
+
while (peek(tokens, state) && peek(tokens, state).value !== "}")
|
|
228
|
+
{
|
|
229
|
+
const keyToken = next(tokens, state)!;
|
|
230
|
+
const isInvalid : boolean = !keyToken || !(keyToken.type === "Identifier" || keyToken.type === "StringLiteral" || keyToken.type === "Literal");
|
|
231
|
+
|
|
232
|
+
if (isInvalid)
|
|
233
|
+
throw new parseErrors.UnexpectedTokenError(keyToken?.value, keyToken?.row ?? row, keyToken?.column ?? column);
|
|
234
|
+
|
|
235
|
+
if (!peek(tokens, state) || peek(tokens, state).value !== ":")
|
|
236
|
+
throw new parseErrors.MissingTokenError(":", keyToken.row, keyToken.column);
|
|
237
|
+
next(tokens, state); // eat :
|
|
238
|
+
|
|
239
|
+
const value = parseExpression(tokens, 0, state, true);
|
|
240
|
+
|
|
241
|
+
properties.unshift({key: keyToken.value, value});
|
|
242
|
+
|
|
243
|
+
const peekToken = peek(tokens, state);
|
|
244
|
+
if (peekToken && peekToken.value === ",")
|
|
245
|
+
next(tokens, state);
|
|
246
|
+
else if (peekToken && peekToken.value !== "}")
|
|
247
|
+
throw new parseErrors.UnexpectedTokenError(peekToken.value, peekToken.row, peekToken.column);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!peek(tokens, state) || peek(tokens, state).value !== "}")
|
|
251
|
+
throw new parseErrors.MissingTokenError("}", token.row, token.column);
|
|
252
|
+
|
|
253
|
+
next(tokens, state); // eat }
|
|
254
|
+
|
|
255
|
+
const node =
|
|
256
|
+
{
|
|
257
|
+
type: "ObjectLiteral",
|
|
258
|
+
properties,
|
|
259
|
+
row,
|
|
260
|
+
column
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
return node;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (value === ";")
|
|
267
|
+
return null;
|
|
268
|
+
|
|
269
|
+
throw new parseErrors.UnexpectedTokenError(value, row, column);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// parse the expression using shunting-yard
|
|
273
|
+
export function parseExpression(tokens : Tokens, minimumPrecedence = 0, state : State, stopAtComma = false) : ParserToken | null
|
|
274
|
+
{
|
|
275
|
+
let operatorStack : Tokens = [];
|
|
276
|
+
let outputStack : ParserToken[] = [];
|
|
277
|
+
|
|
278
|
+
let token = next(tokens, state);
|
|
279
|
+
if (!token)
|
|
280
|
+
return null;
|
|
281
|
+
|
|
282
|
+
let node = parseStartingToken(token, tokens, state)!;
|
|
283
|
+
node = helpers.attach(node, tokens, state, stopAtComma);
|
|
284
|
+
outputStack.push(node);
|
|
285
|
+
while (true)
|
|
286
|
+
{
|
|
287
|
+
let token = peek(tokens, state);
|
|
288
|
+
if (helpers.stoppingCheck(token, stopAtComma))
|
|
289
|
+
break;
|
|
290
|
+
|
|
291
|
+
const operatorInfo = OPERATORS[token.value];
|
|
292
|
+
if (!operatorInfo || operatorInfo.prec < minimumPrecedence)
|
|
293
|
+
break;
|
|
294
|
+
|
|
295
|
+
const operator = next(tokens, state)!;
|
|
296
|
+
|
|
297
|
+
while (outputStack.length > 0)
|
|
298
|
+
{
|
|
299
|
+
const topOperator : BaseToken = operatorStack[operatorStack.length - 1]!;
|
|
300
|
+
const topInfo : Operator | null = OPERATORS[topOperator?.value] ?? null;
|
|
301
|
+
if (!topInfo)
|
|
302
|
+
break;
|
|
303
|
+
|
|
304
|
+
if (
|
|
305
|
+
topInfo.type !== "assignment" && (
|
|
306
|
+
(operatorInfo.assoc === "left" && operatorInfo.prec <= topInfo.prec) ||
|
|
307
|
+
(operatorInfo.assoc === "right" && operatorInfo.prec < topInfo.prec)
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
{
|
|
311
|
+
if (topInfo.type === "unary")
|
|
312
|
+
{
|
|
313
|
+
const operand = outputStack.pop()!;
|
|
314
|
+
outputStack.push
|
|
315
|
+
(
|
|
316
|
+
{
|
|
317
|
+
type: "UnaryExpression",
|
|
318
|
+
value: topOperator.value,
|
|
319
|
+
argument: operand,
|
|
320
|
+
row: topOperator.row,
|
|
321
|
+
column: topOperator.column
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
else
|
|
326
|
+
{
|
|
327
|
+
const rightNode : ParserToken = outputStack.pop()!;
|
|
328
|
+
const leftNode : ParserToken = outputStack.pop()!;
|
|
329
|
+
|
|
330
|
+
outputStack.push
|
|
331
|
+
(
|
|
332
|
+
{
|
|
333
|
+
type: topInfo.type === "logical" ? "LogicalExpression" : "BinaryExpression",
|
|
334
|
+
operator: topOperator.value,
|
|
335
|
+
left: leftNode,
|
|
336
|
+
right: rightNode,
|
|
337
|
+
row: topOperator.row,
|
|
338
|
+
column: topOperator.column
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
operatorStack.pop();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
else
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (operatorInfo.type === "assignment")
|
|
351
|
+
{
|
|
352
|
+
const leftNode = outputStack.pop()!;
|
|
353
|
+
if (leftNode.type !== "VariableReference" && leftNode.type !== "ArrayAccess" && leftNode.type !== "MemberExpression")
|
|
354
|
+
throw new parseErrors.AssignmentError(operator.value, operator.row, operator.column);
|
|
355
|
+
|
|
356
|
+
const rightNode = parseExpression(tokens, operatorInfo.prec, state, stopAtComma);
|
|
357
|
+
|
|
358
|
+
const baseOperators : Record<string, string> =
|
|
359
|
+
{
|
|
360
|
+
"+=": "+",
|
|
361
|
+
"-=": "-",
|
|
362
|
+
"*=": "*",
|
|
363
|
+
"/=": "/",
|
|
364
|
+
"%=": "%",
|
|
365
|
+
"&=": "&",
|
|
366
|
+
"|=": "|",
|
|
367
|
+
"^=": "^",
|
|
368
|
+
"<<=": "<<",
|
|
369
|
+
">>=": ">>",
|
|
370
|
+
">>>=": ">>>",
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const name = leftNode.type === "ArrayAccess" || leftNode.type === "MemberExpression"
|
|
374
|
+
? leftNode
|
|
375
|
+
: leftNode.value
|
|
376
|
+
const valueType = OPERATORS[baseOperators[operator.value]]?.type === "logical"
|
|
377
|
+
? "LogicalExpression"
|
|
378
|
+
: "BinaryExpression";
|
|
379
|
+
outputStack.push
|
|
380
|
+
(
|
|
381
|
+
{
|
|
382
|
+
type: "Assignment",
|
|
383
|
+
name,
|
|
384
|
+
value: operator.value === "="
|
|
385
|
+
? rightNode
|
|
386
|
+
: {
|
|
387
|
+
type: valueType,
|
|
388
|
+
operator: baseOperators[operator.value],
|
|
389
|
+
left: leftNode,
|
|
390
|
+
right: rightNode
|
|
391
|
+
},
|
|
392
|
+
row: operator.row,
|
|
393
|
+
column: operator.column
|
|
394
|
+
}
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
operatorStack.push(operator);
|
|
401
|
+
|
|
402
|
+
let nextToken = next(tokens, state);
|
|
403
|
+
if (!nextToken)
|
|
404
|
+
throw new parseErrors.UnexpectedTokenError("end of input", operator.row, operator.column);
|
|
405
|
+
|
|
406
|
+
let node = parseStartingToken(nextToken, tokens, state);
|
|
407
|
+
if (!node)
|
|
408
|
+
throw new parseErrors.UnexpectedTokenError(nextToken.value, nextToken.row, nextToken.column);
|
|
409
|
+
|
|
410
|
+
node = helpers.attach(node, tokens, state, stopAtComma);
|
|
411
|
+
|
|
412
|
+
outputStack.push(node);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
while (operatorStack.length)
|
|
416
|
+
{
|
|
417
|
+
const operator = operatorStack.pop()!;
|
|
418
|
+
const operatorInfo = OPERATORS[operator.value];
|
|
419
|
+
if (operatorInfo.type === "unary")
|
|
420
|
+
{
|
|
421
|
+
const operand = outputStack.pop(); // take one value
|
|
422
|
+
outputStack.push
|
|
423
|
+
(
|
|
424
|
+
{
|
|
425
|
+
type: "UnaryExpression",
|
|
426
|
+
value: operator.value,
|
|
427
|
+
argument: operand,
|
|
428
|
+
row: operator.row,
|
|
429
|
+
column: operator.column
|
|
430
|
+
}
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
else
|
|
434
|
+
{
|
|
435
|
+
const rightNode : ParserToken = outputStack.pop()!; // take 2 values
|
|
436
|
+
const leftNode : ParserToken = outputStack.pop()!;
|
|
437
|
+
outputStack.push
|
|
438
|
+
(
|
|
439
|
+
{
|
|
440
|
+
type: operatorInfo.type === "logical" ? "LogicalExpression" : "BinaryExpression",
|
|
441
|
+
operator: operator.value,
|
|
442
|
+
left: leftNode,
|
|
443
|
+
right: rightNode,
|
|
444
|
+
row: operator.row,
|
|
445
|
+
column: operator.column
|
|
446
|
+
}
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const maybeTernary = peek(tokens, state);
|
|
452
|
+
if (maybeTernary && maybeTernary.value === "?")
|
|
453
|
+
{
|
|
454
|
+
next(tokens, state); // eat ?
|
|
455
|
+
|
|
456
|
+
const thenBranch = parseExpression(tokens, 0, state, stopAtComma)!;
|
|
457
|
+
|
|
458
|
+
const elseColon = peek(tokens, state);
|
|
459
|
+
if (!elseColon || elseColon.value !== ":")
|
|
460
|
+
throw new parseErrors.MissingTokenError(":", elseColon?.row ?? 0, elseColon?.column ?? 0);
|
|
461
|
+
next(tokens, state); // eat :
|
|
462
|
+
|
|
463
|
+
const elseBranch = parseExpression(tokens, 0, state, stopAtComma)!;
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
type: "TernaryExpression",
|
|
467
|
+
condition: outputStack[0],
|
|
468
|
+
then: thenBranch,
|
|
469
|
+
else: elseBranch,
|
|
470
|
+
row: maybeTernary.row,
|
|
471
|
+
column: maybeTernary.column
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return outputStack[0];
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function parseFunctionCall(name : string, tokens : Tokens, state : State) : ParserToken
|
|
479
|
+
{
|
|
480
|
+
const row = state.lastToken?.row ?? 0;
|
|
481
|
+
const column = state.lastToken?.column ?? 0;
|
|
482
|
+
|
|
483
|
+
next(tokens, state); // eat (
|
|
484
|
+
|
|
485
|
+
let args : ParserToken[] = [];
|
|
486
|
+
|
|
487
|
+
const peekToken = peek(tokens, state);
|
|
488
|
+
|
|
489
|
+
if (peekToken && peekToken.value === ")")
|
|
490
|
+
{
|
|
491
|
+
next(tokens, state); // eat )
|
|
492
|
+
return {
|
|
493
|
+
type: "FunctionCall",
|
|
494
|
+
name,
|
|
495
|
+
args,
|
|
496
|
+
row,
|
|
497
|
+
column
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// get all the arguments
|
|
502
|
+
while (peek(tokens, state) && peek(tokens, state).value !== ")")
|
|
503
|
+
{
|
|
504
|
+
if (peek(tokens, state)?.value === "...")
|
|
505
|
+
{
|
|
506
|
+
next(tokens, state);
|
|
507
|
+
const expression = parseExpression(tokens, 0, state, true);
|
|
508
|
+
args.push
|
|
509
|
+
(
|
|
510
|
+
{
|
|
511
|
+
type: "SpreadElement",
|
|
512
|
+
argument: expression,
|
|
513
|
+
row,
|
|
514
|
+
column
|
|
515
|
+
}
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
else
|
|
519
|
+
{
|
|
520
|
+
const expression = parseExpression(tokens, 0, state, true);
|
|
521
|
+
|
|
522
|
+
if (!expression)
|
|
523
|
+
{
|
|
524
|
+
console.warn(`\n Expected expression in function call "${name}"`);
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
args.push(expression);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const peekToken = peek(tokens, state);
|
|
532
|
+
|
|
533
|
+
if (peekToken && peekToken.value === ",")
|
|
534
|
+
next(tokens, state);
|
|
535
|
+
else if (peekToken && peekToken.value !== ")")
|
|
536
|
+
throw new parseErrors.UnexpectedTokenError(peekToken.value, peekToken.row, peekToken.column);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (!peek(tokens, state) || peek(tokens, state).value !== ")")
|
|
540
|
+
{
|
|
541
|
+
const lastToken = tokens[state.position - 1];
|
|
542
|
+
const row = lastToken?.row ?? 0;
|
|
543
|
+
const column = lastToken?.column ?? 0;
|
|
544
|
+
|
|
545
|
+
throw new parseErrors.FunctionCallError(`Missing ")" in function call for name "${name}"`, row, column);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
next(tokens, state); // eat )
|
|
549
|
+
|
|
550
|
+
if (peek(tokens, state) && peek(tokens, state).value === "(")
|
|
551
|
+
throw new parseErrors.ChainedFunctionCallError(name, peek(tokens, state).row, peek(tokens, state).column);
|
|
552
|
+
|
|
553
|
+
return {
|
|
554
|
+
type: "FunctionCall",
|
|
555
|
+
name,
|
|
556
|
+
args,
|
|
557
|
+
row,
|
|
558
|
+
column
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export function parseStatement(ast : AST, tokens : Tokens, state : State) : true | ParserToken | null
|
|
563
|
+
{
|
|
564
|
+
const token = peek(tokens, state);
|
|
565
|
+
if (!token)
|
|
566
|
+
return null;
|
|
567
|
+
|
|
568
|
+
// run all the handlers for keywords
|
|
569
|
+
if (token.type === "Keyword")
|
|
570
|
+
{
|
|
571
|
+
if (handlers.variableHandler(ast, token, tokens, state)) return true;
|
|
572
|
+
else if (handlers.ifHandler(ast, token, tokens, state)) return true;
|
|
573
|
+
else if (handlers.doWhileHandler(ast, token, tokens, state)) return true;
|
|
574
|
+
else if (handlers.whileHandler(ast, token, tokens, state)) return true;
|
|
575
|
+
else if (handlers.loopControlHandler(ast, token, tokens, state)) return true;
|
|
576
|
+
else if (handlers.forHandler(ast, token, tokens, state)) return true;
|
|
577
|
+
else if (handlers.functionHandler(ast, token, tokens, state)) return true;
|
|
578
|
+
else if (handlers.returnHandler(ast, token, tokens, state)) return true;
|
|
579
|
+
else if (handlers.importHandler(ast, token, tokens, state)) return true;
|
|
580
|
+
else if (handlers.classHandler(ast, token, tokens, state)) return true;
|
|
581
|
+
else if (handlers.tryHandler(ast, token, tokens, state)) return true;
|
|
582
|
+
else if (handlers.switchHandler(ast, token, tokens, state)) return true;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const expression = parseExpression(tokens, 0, state)!;
|
|
586
|
+
if (expression)
|
|
587
|
+
ast.body.push(expression);
|
|
588
|
+
|
|
589
|
+
return expression;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export function parser(tokens : Tokens) : AST
|
|
593
|
+
{
|
|
594
|
+
let state : State =
|
|
595
|
+
{
|
|
596
|
+
position: 0,
|
|
597
|
+
time: 0,
|
|
598
|
+
lastToken: null
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
let ast : AST =
|
|
602
|
+
{
|
|
603
|
+
type: "Program",
|
|
604
|
+
body:
|
|
605
|
+
[
|
|
606
|
+
|
|
607
|
+
]
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
while (state.position < tokens.length)
|
|
611
|
+
{
|
|
612
|
+
const token = peek(tokens, state);
|
|
613
|
+
if (!token)
|
|
614
|
+
break;
|
|
615
|
+
|
|
616
|
+
parseStatement(ast, tokens, state);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
return ast;
|
|
620
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
const counterFile = path.join(process.env.LOCALAPPDATA || process.cwd(), "pure-dango", "quote_crimes.json");
|
|
5
|
+
|
|
6
|
+
export function getQuoteCrimes() : number
|
|
7
|
+
{
|
|
8
|
+
try
|
|
9
|
+
{
|
|
10
|
+
const data = fs.readFileSync(counterFile, "utf8");
|
|
11
|
+
return JSON.parse(data).count ?? 0;
|
|
12
|
+
}
|
|
13
|
+
catch
|
|
14
|
+
{
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resetQuoteCrimes()
|
|
20
|
+
{
|
|
21
|
+
const zero = 0;
|
|
22
|
+
fs.mkdirSync(path.dirname(counterFile), {recursive: true});
|
|
23
|
+
fs.writeFileSync(counterFile, JSON.stringify({zero}));
|
|
24
|
+
return zero;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function incrementQuoteCrimes() : number
|
|
28
|
+
{
|
|
29
|
+
const count = getQuoteCrimes() + 1;
|
|
30
|
+
fs.mkdirSync(path.dirname(counterFile), {recursive: true});
|
|
31
|
+
fs.writeFileSync(counterFile, JSON.stringify({count}));
|
|
32
|
+
return count;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function QuoteCrimesMessage(count: number, opening : string) : string
|
|
36
|
+
{
|
|
37
|
+
if (count <= 1) return `Oops you broke the quotes! Quote was opened with ${opening}, but closed with another ${opening}.`
|
|
38
|
+
if (count <= 3) return `Oops you broke the quotes, again. Quote was opened with ${opening}, but closed with another ${opening}.`;
|
|
39
|
+
if (count <= 5) return `You broke the quotes AGAIN. This is the ${count}th time. Do you even read the errors?`;
|
|
40
|
+
if (count <= 9) return `I'm starting to think you're doing this on purpose`;
|
|
41
|
+
if (count <= 14) return `At this point just use straight quotes. It's gonna benefit both of us`;
|
|
42
|
+
return `${count} times.`;
|
|
43
|
+
}
|