Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/gravity
Path: blob/master/src/compiler/gravity_parser.c
1214 views
1
//
2
// gravity_parser.c
3
// gravity
4
//
5
// Created by Marco Bambini on 01/09/14.
6
// Copyright (c) 2014 CreoLabs. All rights reserved.
7
//
8
9
#include "gravity_symboltable.h"
10
#include "gravity_parser.h"
11
#include "gravity_macros.h"
12
#include "gravity_lexer.h"
13
#include "gravity_token.h"
14
#include "gravity_utils.h"
15
#include "gravity_array.h"
16
#include "gravity_core.h"
17
#include "gravity_ast.h"
18
19
typedef marray_t(gravity_lexer_t*) lexer_r;
20
21
struct gravity_parser_t {
22
lexer_r *lexer;
23
gnode_r *statements;
24
uint16_r declarations;
25
gravity_delegate_t *delegate;
26
27
double time;
28
uint32_t nerrors;
29
uint32_t unique_id;
30
uint32_t last_error_lineno;
31
32
// state ivars used by Pratt parser
33
gtoken_t current_token;
34
gnode_t *current_node;
35
};
36
37
// MARK: - PRATT parser specs -
38
// http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
39
// http://javascript.crockford.com/tdop/tdop.html
40
41
// Precedence table as defined in Swift
42
// http://nshipster.com/swift-operators/
43
typedef enum {
44
PREC_LOWEST,
45
PREC_ASSIGN = 90, // = *= /= %= += -= <<= >>= &= ^= |= (11 cases)
46
PREC_TERNARY = 100, // ?: (1 case)
47
PREC_LOGICAL_OR = 110, // || (1 case)
48
PREC_LOGICAL_AND = 120, // && (1 case)
49
PREC_COMPARISON = 130, // < <= > >= == != === !== ~= (9 cases)
50
PREC_ISA = 132, // isa (1 case)
51
PREC_RANGE = 135, // ..< ... (2 cases)
52
PREC_TERM = 140, // + - | ^ (4 cases)
53
PREC_FACTOR = 150, // * / % & (4 cases)
54
PREC_SHIFT = 160, // << >> (2 cases)
55
PREC_UNARY = 170, // + - ! ~ (4 cases)
56
PREC_CALL = 200 // . ( [ (3 cases)
57
} prec_level;
58
59
typedef gnode_t* (*parse_func) (gravity_parser_t *parser);
60
61
typedef struct {
62
parse_func prefix;
63
parse_func infix;
64
prec_level precedence;
65
const char *name;
66
bool right;
67
} grammar_rule;
68
69
// This table defines all of the parsing rules for the prefix and infix expressions in the grammar.
70
#define RULE(prec, fn1, fn2) (grammar_rule){ fn1, fn2, prec, NULL, false}
71
#define PREFIX(prec, fn) (grammar_rule){ fn, NULL, prec, NULL, false}
72
#define INFIX(prec, fn) (grammar_rule){ NULL, fn, prec, NULL, false}
73
#define INFIX_OPERATOR(prec, name) (grammar_rule){ NULL, parse_infix, prec, name, false}
74
#define INFIX_OPERATOR_RIGHT(prec,name) (grammar_rule){ NULL, parse_infix, prec, name, true}
75
#define PREFIX_OPERATOR(name) (grammar_rule){ parse_unary, NULL, PREC_LOWEST, name, false}
76
#define OPERATOR(prec, name) (grammar_rule){ parse_unary, parse_infix, prec, name, false}
77
78
// Global singleton grammar rule table
79
static grammar_rule rules[TOK_END];
80
81
// MARK: - Internal macros -
82
#define SEMICOLON_IS_OPTIONAL 1
83
84
#define REPORT_ERROR(_tok,...) report_error(parser, GRAVITY_ERROR_SYNTAX, _tok, __VA_ARGS__)
85
#define REPORT_WARNING(_tok,...) report_error(parser, GRAVITY_WARNING, _tok, __VA_ARGS__)
86
87
#define PUSH_DECLARATION(_decl) marray_push(uint16_t, parser->declarations, (uint16_t)_decl)
88
#define PUSH_FUNCTION_DECLARATION() PUSH_DECLARATION(NODE_FUNCTION_DECL)
89
#define PUSH_CLASS_DECLARATION() PUSH_DECLARATION(NODE_CLASS_DECL)
90
#define POP_DECLARATION() marray_pop(parser->declarations)
91
#define LAST_DECLARATION() (marray_size(parser->declarations) ? marray_last(parser->declarations) : 0)
92
#define IS_FUNCTION_ENCLOSED() (LAST_DECLARATION() == NODE_FUNCTION_DECL)
93
#define IS_CLASS_ENCLOSED() (LAST_DECLARATION() == NODE_CLASS_DECL)
94
#define CHECK_NODE(_n) if (!_n) return NULL
95
96
#define POP_LEXER (gravity_lexer_t *)marray_pop(*parser->lexer)
97
#define CURRENT_LEXER (gravity_lexer_t *)marray_last(*parser->lexer)
98
#define DECLARE_LEXER gravity_lexer_t *lexer = CURRENT_LEXER; DEBUG_LEXER(lexer)
99
100
#define STATIC_TOKEN_CSTRING(_s,_n,_l,_b,_t) char _s[_n] = {0}; uint32_t _l = 0; \
101
const char *_b = token_string(_t, &_l); \
102
if (_l) memcpy(_s, _b, MINNUM(_n, _l))
103
104
// MARK: - Prototypes -
105
static const char *parse_identifier (gravity_parser_t *parser);
106
static gnode_t *parse_statement (gravity_parser_t *parser);
107
static gnode_r *parse_optional_parameter_declaration (gravity_parser_t *parser);
108
static gnode_t *parse_compound_statement (gravity_parser_t *parser);
109
static gnode_t *parse_expression (gravity_parser_t *parser);
110
static gnode_t *parse_declaration_statement (gravity_parser_t *parser);
111
static gnode_t *parse_function (gravity_parser_t *parser, bool is_declaration, gtoken_t access_specifier, gtoken_t storage_specifier);
112
static gnode_t *adjust_assignment_expression (gtoken_t tok, gnode_t *lnode, gnode_t *rnode);
113
114
// MARK: - Utils functions -
115
116
static void report_error (gravity_parser_t *parser, error_type_t error_type, gtoken_s token, const char *format, ...) {
117
// consider just one error for each line;
118
if (parser->last_error_lineno == token.lineno) return;
119
parser->last_error_lineno = token.lineno;
120
121
// increment internal error counter
122
++parser->nerrors;
123
124
// get error callback (if any)
125
void *data = (parser->delegate) ? parser->delegate->xdata : NULL;
126
gravity_error_callback error_fn = (parser->delegate) ? ((gravity_delegate_t *)parser->delegate)->error_callback : NULL;
127
128
// build error message
129
char buffer[1024];
130
va_list arg;
131
if (format) {
132
va_start (arg, format);
133
vsnprintf(buffer, sizeof(buffer), format, arg);
134
va_end (arg);
135
}
136
137
// setup error struct
138
error_desc_t error_desc = {
139
.code = 0,
140
.lineno = token.lineno,
141
.colno = token.colno,
142
.fileid = token.fileid,
143
.offset = token.position
144
};
145
146
// finally call error callback
147
if (error_fn) error_fn(error_type, buffer, error_desc, data);
148
else printf("%s\n", buffer);
149
}
150
151
static gnode_t *parse_error (gravity_parser_t *parser) {
152
DECLARE_LEXER;
153
gravity_lexer_next(lexer);
154
gtoken_s token = gravity_lexer_token(lexer);
155
REPORT_ERROR(token, "%s", token.value);
156
return NULL;
157
}
158
159
// RETURN:
160
// - true if next token is equal to token passed as parameter (token is also consumed)
161
// - false if next token is not equal (and no error is reported)
162
//
163
static bool parse_optional (gravity_parser_t *parser, gtoken_t token) {
164
DECLARE_LEXER;
165
166
gtoken_t peek = gravity_lexer_peek(lexer);
167
if (token_iserror(peek)) {
168
parse_error(parser);
169
peek = gravity_lexer_peek(lexer);
170
}
171
172
if (peek == token) {
173
gravity_lexer_next(lexer); // consume expected token
174
return true;
175
}
176
177
// do not report any error in this case
178
return false;
179
}
180
181
static bool parse_required (gravity_parser_t *parser, gtoken_t token) {
182
if (parse_optional(parser, token)) return true;
183
184
// token not found (and not consumed) so an error strategy must be implemented here
185
186
// simple (but not simpler) error recovery
187
// parser should keep track of what I am parsing
188
// so based on tok I could have a token list of terminal symbols
189
// call next until first sync symbol (or EOF or start of another terminal symbol is found)
190
191
// simple error recovery, just consume next and report error
192
DECLARE_LEXER;
193
gtoken_t next = gravity_lexer_next(lexer);
194
gtoken_s unexpected_token = gravity_lexer_token(lexer);
195
REPORT_ERROR(unexpected_token, "Expected %s but found %s.", token_name(token), token_name(next));
196
return false;
197
}
198
199
static bool parse_semicolon (gravity_parser_t *parser) {
200
#if SEMICOLON_IS_OPTIONAL
201
DECLARE_LEXER;
202
if (gravity_lexer_peek(lexer) == TOK_OP_SEMICOLON) {gravity_lexer_next(lexer); return true;}
203
return false;
204
#else
205
return parse_required(parser, TOK_OP_SEMICOLON);
206
#endif
207
}
208
209
gnode_t *parse_function (gravity_parser_t *parser, bool is_declaration, gtoken_t access_specifier, gtoken_t storage_specifier) {
210
DECLARE_LEXER;
211
212
// access_specifier? storage_specifier? already parsed
213
// 'function' IDENTIFIER '(' parameter_declaration_clause? ')' compound_statement
214
215
// consume FUNC keyword (or peek for OPEN_CURLYBRACE)
216
bool is_implicit = (gravity_lexer_peek(lexer) == TOK_OP_OPEN_CURLYBRACE);
217
gtoken_s token = gravity_lexer_token(lexer);
218
if (!is_implicit) {
219
gtoken_t type = gravity_lexer_next(lexer);
220
token = gravity_lexer_token(lexer);
221
assert(type == TOK_KEY_FUNC);
222
}
223
224
// parse IDENTIFIER
225
const char *identifier = NULL;
226
if (is_declaration) {
227
gtoken_t peek = gravity_lexer_peek(lexer);
228
if (token_isoperator(peek)) {
229
gravity_lexer_next(lexer);
230
identifier = string_dup(token_name(peek));
231
} else {
232
identifier = parse_identifier(parser);
233
}
234
DEBUG_PARSER("parse_function_declaration %s", identifier);
235
}
236
237
// check and consume TOK_OP_OPEN_PARENTHESIS
238
if (!is_implicit) parse_required(parser, TOK_OP_OPEN_PARENTHESIS);
239
240
// parse optional parameter declaration clause
241
gnode_r *params = (!is_implicit) ? parse_optional_parameter_declaration(parser) : NULL;
242
243
// check and consume TOK_OP_CLOSED_PARENTHESIS
244
if (!is_implicit) parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
245
246
// parse compound statement
247
PUSH_FUNCTION_DECLARATION();
248
gnode_compound_stmt_t *compound = (gnode_compound_stmt_t*)parse_compound_statement(parser);
249
POP_DECLARATION();
250
251
// parse optional semicolon
252
parse_semicolon(parser);
253
254
return gnode_function_decl_create(token, identifier, access_specifier, storage_specifier, params, compound);
255
}
256
257
static char *cstring_from_token (gravity_parser_t *parser, gtoken_s token) {
258
#pragma unused(parser)
259
uint32_t len = 0;
260
const char *buffer = token_string(token, &len);
261
262
char *str = (char *)mem_alloc(len+1);
263
memcpy(str, buffer, len);
264
return str;
265
}
266
267
static gnode_t *local_store_declaration (const char *identifier, gtoken_t access_specifier, gtoken_t storage_specifier, gnode_t *declaration) {
268
gnode_r *decls = gnode_array_create();
269
gnode_t *decl = gnode_variable_create(declaration->token, identifier ? string_dup(identifier) : NULL, NULL, access_specifier, declaration);
270
gnode_array_push(decls, decl);
271
return gnode_variable_decl_create(declaration->token, TOK_KEY_VAR, access_specifier, storage_specifier, decls);
272
}
273
274
static gliteral_t decode_number_binary (gtoken_s token, int64_t *n) {
275
// from 2 in order to skip 0b
276
*n = number_from_bin(&token.value[2], token.bytes-2);
277
return LITERAL_INT;
278
}
279
280
static gliteral_t decode_number_octal (gtoken_s token, int64_t *n) {
281
STATIC_TOKEN_CSTRING(str, 512, len, buffer, token);
282
if (len) *n = (int64_t) number_from_oct(&str[2], len-2);
283
return LITERAL_INT;
284
}
285
286
static gliteral_t decode_number_hex (gtoken_s token, int64_t *n, double *d) {
287
#pragma unused(d)
288
STATIC_TOKEN_CSTRING(str, 512, len, buffer, token);
289
if (len) *n = (int64_t) number_from_hex(str, token.bytes);
290
return LITERAL_INT;
291
}
292
293
// MARK: - Expressions -
294
295
static gnode_t *parse_ternary_expression (gravity_parser_t *parser) {
296
DEBUG_PARSER("parse_ternary_expression");
297
DECLARE_LEXER;
298
299
// conditional expression already parsed
300
gnode_t *cond = parser->current_node;
301
if (!cond) return NULL;
302
303
// '?' expression ':' expression
304
305
// '?' already consumed
306
gtoken_s token = gravity_lexer_token(lexer);
307
308
// parse expression 1
309
gnode_t *expr1 = parse_expression(parser);
310
CHECK_NODE(expr1);
311
312
parse_required(parser, TOK_OP_COLON);
313
314
// parse expression 2
315
gnode_t *expr2 = parse_expression(parser);
316
CHECK_NODE(expr2);
317
318
return gnode_flow_stat_create(token, cond, expr1, expr2);
319
}
320
321
static gnode_t *parse_file_expression (gravity_parser_t *parser) {
322
DEBUG_PARSER("parse_file_expression");
323
DECLARE_LEXER;
324
325
// at least one identifier is mandatory
326
// 'file' ('.' IDENTIFIER)+
327
328
gravity_lexer_next(lexer);
329
gtoken_s token = gravity_lexer_token(lexer);
330
331
if (gravity_lexer_peek(lexer) != TOK_OP_DOT) {
332
REPORT_ERROR(token, "A .identifier list is expected here.");
333
return NULL;
334
}
335
336
cstring_r *list = cstring_array_create();
337
while (gravity_lexer_peek(lexer) == TOK_OP_DOT) {
338
gravity_lexer_next(lexer); // consume TOK_OP_DOT
339
const char *identifier = parse_identifier(parser);
340
if (!identifier) return NULL;
341
cstring_array_push(list, identifier);
342
}
343
344
return gnode_file_expr_create(token, list);
345
}
346
347
static const char *parse_identifier (gravity_parser_t *parser) {
348
DECLARE_LEXER;
349
350
// parse IDENTIFIER is always mandatory
351
gtoken_t type = gravity_lexer_peek(lexer);
352
if (type != TOK_IDENTIFIER) {
353
if (type == TOK_ERROR) parse_error(parser);
354
else REPORT_ERROR(gravity_lexer_token(lexer), "Expected identifier but found %s", token_name(type));
355
return NULL;
356
}
357
358
gravity_lexer_next(lexer);
359
gtoken_s token = gravity_lexer_token(lexer);
360
const char *identifier = cstring_from_token(parser, token);
361
return identifier;
362
}
363
364
static const char *parse_optional_type_annotation (gravity_parser_t *parser) {
365
DECLARE_LEXER;
366
const char *type_annotation = NULL;
367
gtoken_t peek = gravity_lexer_peek(lexer);
368
369
// type annotation
370
// function foo (a: string, b: number)
371
372
// check for optional type_annotation
373
if (peek == TOK_OP_COLON) {
374
gravity_lexer_next(lexer); // consume TOK_OP_COLON
375
376
// parse identifier
377
type_annotation = parse_identifier(parser);
378
if (!type_annotation) return NULL;
379
}
380
381
return type_annotation;
382
}
383
384
static gnode_t *parse_parentheses_expression (gravity_parser_t *parser) {
385
DEBUG_PARSER("parse_parentheses_expression");
386
387
// check and consume TOK_OP_OPEN_PARENTHESIS
388
parse_required(parser, TOK_OP_OPEN_PARENTHESIS);
389
390
// parse expression
391
gnode_t *expr = parse_expression(parser);
392
CHECK_NODE(expr);
393
394
// check and consume TOK_OP_CLOSED_PARENTHESIS
395
parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
396
397
return expr;
398
}
399
400
static gnode_t *parse_list_expression (gravity_parser_t *parser) {
401
DEBUG_PARSER("parse_list_expression");
402
DECLARE_LEXER;
403
404
/*
405
list_expression
406
: '[' ((expression) (',' expression)*)? ']' // array or empty array
407
| '[' ((map_entry (',' map_entry)*) | ':') ']' // map or empty map
408
;
409
410
map_entry
411
: STRING ':' expression
412
;
413
*/
414
415
// consume first '['
416
parse_required(parser, TOK_OP_OPEN_SQUAREBRACKET);
417
418
// this saved token is necessary to save start of the list/map
419
gtoken_s token = gravity_lexer_token(lexer);
420
421
// check for special empty list
422
if (gravity_lexer_peek(lexer) == TOK_OP_CLOSED_SQUAREBRACKET) {
423
gravity_lexer_next(lexer); // consume TOK_OP_CLOSED_SQUAREBRACKET
424
return gnode_list_expr_create(token, NULL, NULL, false);
425
}
426
427
// check for special empty map
428
if (gravity_lexer_peek(lexer) == TOK_OP_COLON) {
429
gravity_lexer_next(lexer); // consume TOK_OP_COLON
430
parse_required(parser, TOK_OP_CLOSED_SQUAREBRACKET);
431
return gnode_list_expr_create(token, NULL, NULL, true);
432
}
433
434
// parse first expression (if any) outside of the list/map loop
435
// in order to check if it is a list or map expression
436
gnode_t *expr1 = parse_expression(parser);
437
438
// if next token is a colon then assume a map
439
bool ismap = (gravity_lexer_peek(lexer) == TOK_OP_COLON);
440
441
// a list expression can be an array [expr1, expr2] or a map [string1: expr1, string2: expr2]
442
// cannot be mixed so be very restrictive here
443
444
gnode_r *list1 = gnode_array_create();
445
gnode_r *list2 = (ismap) ? gnode_array_create() : NULL;
446
if (expr1) gnode_array_push(list1, expr1);
447
448
if (ismap) {
449
parse_required(parser, TOK_OP_COLON);
450
gnode_t *expr2 = parse_expression(parser);
451
if (expr2) gnode_array_push(list2, expr2);
452
}
453
454
while (gravity_lexer_peek(lexer) == TOK_OP_COMMA) {
455
gravity_lexer_next(lexer); // consume TOK_OP_COMMA
456
457
// parse first expression
458
expr1 = parse_expression(parser);
459
if (expr1) gnode_array_push(list1, expr1);
460
461
if (ismap) {
462
parse_required(parser, TOK_OP_COLON);
463
gnode_t *expr2 = parse_expression(parser);
464
if (expr2) gnode_array_push(list2, expr2);
465
}
466
}
467
468
parse_required(parser, TOK_OP_CLOSED_SQUAREBRACKET);
469
return gnode_list_expr_create(token, list1, list2, ismap);
470
}
471
472
static gnode_t *parse_function_expression (gravity_parser_t *parser) {
473
DEBUG_PARSER("parse_function_expression");
474
475
// 'func' '(' parameter_declaration_clause? ')' compound_statement
476
// or
477
// compound_statement (implicit func and implicit parameters)
478
/*
479
example:
480
func foo () {
481
var bar = func(x) {return x*2;}
482
return bar(3);
483
}
484
485
it is equivalent to:
486
487
func foo () {
488
func bar(x) {return x*2;}
489
return bar(3);
490
}
491
492
*/
493
494
// check if func is a function expression or
495
// if it is a func keyword used to refers to
496
// the current executing function
497
498
return parse_function(parser, false, 0, 0);
499
}
500
501
static gnode_t *parse_identifier_expression (gravity_parser_t *parser) {
502
DEBUG_PARSER("parse_identifier_expression");
503
DECLARE_LEXER;
504
505
const char *identifier = parse_identifier(parser);
506
if (!identifier) return NULL;
507
DEBUG_PARSER("IDENTIFIER: %s", identifier);
508
509
gtoken_s token = gravity_lexer_token(lexer);
510
return gnode_identifier_expr_create(token, identifier, NULL);
511
}
512
513
static gnode_t *parse_number_expression (gtoken_s token) {
514
DEBUG_PARSER("parse_number_expression");
515
516
// what I know here is that token is a well formed NUMBER
517
// so I just need to properly decode it
518
519
const char *value = token.value;
520
gliteral_t type;
521
int64_t n = 0;
522
double d = 0;
523
524
if (value[0] == '0') {
525
int c = toupper(value[1]);
526
if (c == 'B') {type = decode_number_binary(token, &n); goto report_node;}
527
else if (c == 'O') {type = decode_number_octal(token, &n); goto report_node;}
528
else if (c == 'X') {type = decode_number_hex(token, &n, &d); goto report_node;}
529
}
530
531
// number is decimal (check if is float)
532
bool isfloat = false;
533
for (uint32_t i=0; i<token.bytes; ++i) {
534
if (value[i] == '.') {isfloat = true; break;}
535
}
536
537
STATIC_TOKEN_CSTRING(str, 512, len, buffer, token);
538
if (isfloat) {
539
d = strtod(str, NULL);
540
type = LITERAL_FLOAT;
541
DEBUG_PARSER("FLOAT: %.2f", d);
542
}
543
else {
544
n = (int64_t) strtoll(str, NULL, 0);
545
type = LITERAL_INT;
546
DEBUG_PARSER("INT: %lld", n);
547
}
548
549
report_node:
550
if (type == LITERAL_FLOAT) return gnode_literal_float_expr_create(token, (double)d);
551
else if (type == LITERAL_INT) return gnode_literal_int_expr_create(token, n);
552
else assert(0);
553
554
return NULL;
555
}
556
557
static gnode_t *parse_literal_expression (gravity_parser_t *parser) {
558
DEBUG_PARSER("parse_literal_expression");
559
DECLARE_LEXER;
560
561
gtoken_t type = gravity_lexer_next(lexer);
562
gtoken_s token = gravity_lexer_token(lexer);
563
564
if (type == TOK_STRING) {
565
uint32_t len = 0;
566
const char *value = token_string(token, &len);
567
DEBUG_PARSER("STRING: %.*s", len, value);
568
return gnode_literal_string_expr_create(token, value, len);
569
}
570
571
if (type == TOK_KEY_TRUE || type == TOK_KEY_FALSE) {
572
return gnode_literal_bool_expr_create(token, (int32_t)(type == TOK_KEY_TRUE) ? 1 : 0);
573
}
574
575
if (type != TOK_NUMBER) {
576
REPORT_ERROR(token, "Expected literal expression but found %s.", token_name(type));
577
return NULL;
578
}
579
580
return parse_number_expression(token);
581
}
582
583
static gnode_t *parse_keyword_expression (gravity_parser_t *parser) {
584
DEBUG_PARSER("parse_keyword_expression");
585
DECLARE_LEXER;
586
587
gravity_lexer_next(lexer);
588
gtoken_s token = gravity_lexer_token(lexer);
589
590
return gnode_keyword_expr_create(token);
591
}
592
593
static gnode_r *parse_arguments_expression (gravity_parser_t *parser) {
594
DEBUG_PARSER("parse_call_expression_list");
595
DECLARE_LEXER;
596
597
// it's OK for a call_expression_list to be empty
598
if (gravity_lexer_peek(lexer) == TOK_OP_CLOSED_PARENTHESIS) return NULL;
599
600
gnode_r *list = gnode_array_create();
601
while (1) {
602
gtoken_t peek = gravity_lexer_peek(lexer);
603
604
if (peek == TOK_OP_COMMA) {
605
// added the ability to convert ,, to ,undefined,
606
gnode_array_push(list, gnode_keyword_expr_create(UNDEF_TOKEN));
607
608
// consume next TOK_OP_COMMA and check for special ,) case
609
gravity_lexer_next(lexer);
610
if (gravity_lexer_peek(lexer) == TOK_OP_CLOSED_PARENTHESIS) gnode_array_push(list, gnode_keyword_expr_create(UNDEF_TOKEN));
611
} else {
612
// check exit condition
613
if ((peek == TOK_EOF) || (peek == TOK_OP_CLOSED_PARENTHESIS)) break;
614
615
// parse expression
616
gnode_t *expr = parse_expression(parser);
617
if (expr) gnode_array_push(list, expr);
618
619
// consume next TOK_OP_COMMA and check for special ,) case
620
if (gravity_lexer_peek(lexer) == TOK_OP_COMMA) {
621
gravity_lexer_next(lexer);
622
if (gravity_lexer_peek(lexer) == TOK_OP_CLOSED_PARENTHESIS) gnode_array_push(list, gnode_keyword_expr_create(UNDEF_TOKEN));
623
}
624
}
625
}
626
627
return list;
628
}
629
630
static gnode_t *parse_postfix_expression (gravity_parser_t *parser, gtoken_t tok) {
631
DEBUG_PARSER("parse_postfix_expression");
632
DECLARE_LEXER;
633
634
// '[' assignment_expression ']' => Subscript operator
635
// '(' expression_list? ')' => Function call operator
636
// '.' IDENTIFIER => Member access operator
637
638
// tok already consumed and used to identify postfix sub-expression
639
gnode_t *lnode = parser->current_node;
640
gtoken_s token = gravity_lexer_token(lexer);
641
642
// a postfix expression is an expression followed by a list of other expressions (separated by specific tokens)
643
gnode_r *list = gnode_array_create();
644
while (1) {
645
gnode_t *node = NULL;
646
647
if (tok == TOK_OP_OPEN_SQUAREBRACKET) {
648
gnode_t *expr = parse_expression(parser);
649
gtoken_s subtoken = gravity_lexer_token(lexer);
650
parse_required(parser, TOK_OP_CLOSED_SQUAREBRACKET);
651
node = gnode_postfix_subexpr_create(subtoken, NODE_SUBSCRIPT_EXPR, expr, NULL);
652
} else if (tok == TOK_OP_OPEN_PARENTHESIS) {
653
gnode_r *args = parse_arguments_expression(parser); // can be NULL and it's OK
654
gtoken_s subtoken = gravity_lexer_token(lexer);
655
parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
656
node = gnode_postfix_subexpr_create(subtoken, NODE_CALL_EXPR, NULL, args);
657
} else if (tok == TOK_OP_DOT) {
658
gnode_t *expr = parse_identifier_expression(parser);
659
gtoken_s subtoken = gravity_lexer_token(lexer);
660
node = gnode_postfix_subexpr_create(subtoken, NODE_ACCESS_EXPR, expr, NULL);
661
} else {
662
// should never reach this point
663
assert(0);
664
}
665
666
// add subnode to list
667
gnode_array_push(list, node);
668
669
// check if postifx expression has more sub-nodes
670
gtoken_t peek = gravity_lexer_peek(lexer);
671
if ((peek != TOK_OP_OPEN_SQUAREBRACKET) && (peek != TOK_OP_OPEN_PARENTHESIS) && (peek != TOK_OP_DOT)) break;
672
tok = gravity_lexer_next(lexer);
673
}
674
675
return gnode_postfix_expr_create(token, lnode, list);
676
}
677
678
static gnode_t *parse_postfix_subscript (gravity_parser_t *parser) {
679
// NOTE:
680
// Gravity does not support a syntax like m[1,2] for matrix access (not m[1,2,3])
681
// but it supports a syntax like m[1][2] (or m[1][2][3])
682
683
DEBUG_PARSER("parse_postfix_subscript");
684
return parse_postfix_expression(parser, TOK_OP_OPEN_SQUAREBRACKET);
685
}
686
687
static gnode_t *parse_postfix_access (gravity_parser_t *parser) {
688
DEBUG_PARSER("parse_postfix_access");
689
return parse_postfix_expression(parser, TOK_OP_DOT);
690
}
691
692
static gnode_t *parse_postfix_call (gravity_parser_t *parser) {
693
DEBUG_PARSER("parse_postfix_call");
694
return parse_postfix_expression(parser, TOK_OP_OPEN_PARENTHESIS);
695
}
696
697
static gnode_t *parse_precedence(gravity_parser_t *parser, prec_level precedence) {
698
DEBUG_PARSER("parse_precedence (level %d)", precedence);
699
DECLARE_LEXER;
700
701
// peek next
702
gtoken_t type = gravity_lexer_peek(lexer);
703
if (type == TOK_EOF) return NULL;
704
705
parse_func prefix = rules[type].prefix;
706
if (prefix == NULL) {
707
// gravity_lexer_token reports the latest succesfully scanned token but since we need to report
708
// an error for a peeked token then we force reporting "next" token
709
REPORT_ERROR(gravity_lexer_token_next(lexer), "Expected expression but found %s.", token_name(type));
710
return NULL;
711
}
712
gnode_t *node = prefix(parser);
713
714
gtoken_t peek = gravity_lexer_peek(lexer);
715
if (type == TOK_EOF) return NULL;
716
717
while (precedence < rules[peek].precedence) {
718
gtoken_t tok = gravity_lexer_next(lexer);
719
grammar_rule *rule = &rules[tok];
720
721
parser->current_token = tok;
722
parser->current_node = node;
723
node = rule->infix(parser);
724
725
peek = gravity_lexer_peek(lexer);
726
if (type == TOK_EOF) return NULL;
727
}
728
729
return node;
730
}
731
732
static gnode_t *parse_expression (gravity_parser_t *parser) {
733
DEBUG_PARSER("parse_expression");
734
DECLARE_LEXER;
735
736
// parse_expression is the default case called when no other case in parse_statament can be resolved
737
// due to some syntax errors an infinte loop condition can be verified
738
gtoken_s tok1 = gravity_lexer_token(lexer);
739
740
gnode_t *expr = parse_precedence(parser, PREC_LOWEST);
741
742
// expr is NULL means than an error condition has been encountered (and a potential infite loop)
743
if (expr == NULL) {
744
gtoken_s tok2 = gravity_lexer_token(lexer);
745
// if current token is equal to the token saved before the recursion than skip token in order to avoid infinite loop
746
if (token_identical(&tok1, &tok2)) gravity_lexer_next(lexer);
747
}
748
749
return expr;
750
}
751
752
static gnode_t *parse_unary (gravity_parser_t *parser) {
753
DEBUG_PARSER("parse_unary");
754
DECLARE_LEXER;
755
756
gtoken_t tok = gravity_lexer_next(lexer);
757
gnode_t *node = parse_precedence(parser, PREC_UNARY);
758
return gnode_unary_expr_create(tok, node);
759
}
760
761
static gnode_t *parse_infix (gravity_parser_t *parser) {
762
DEBUG_PARSER("parse_infix");
763
764
gtoken_t tok = parser->current_token;
765
gnode_t *lnode = parser->current_node;
766
767
// we can make right associative operators by reducing the right binding power
768
grammar_rule *rule = &rules[tok];
769
prec_level precedence = (rule->right) ? rule->precedence-1 : rule->precedence;
770
771
gnode_t *rnode = parse_precedence(parser, precedence);
772
if ((tok != TOK_OP_ASSIGN) && token_isassignment(tok)) return adjust_assignment_expression(tok, lnode, rnode);
773
return gnode_binary_expr_create(tok, lnode, rnode);
774
}
775
776
// MARK: -
777
778
static gnode_t *adjust_assignment_expression (gtoken_t tok, gnode_t *lnode, gnode_t *rnode) {
779
DEBUG_PARSER("adjust_assignment_expression");
780
781
// called when tok is an assignment != TOK_OP_ASSIGN
782
// convert expressions:
783
// a += 1 => a = a + 1
784
// a -= 1 => a = a - 1
785
// a *= 1 => a = a * 1
786
// a /= 1 => a = a / 1
787
// a %= 1 => a = a % 1
788
// a <<=1 => a = a << 1
789
// a >>=1 => a = a >> 1
790
// a &= 1 => a = a & 1
791
// a |= 1 => a = a | 1
792
// a ^= 1 => a = a ^ 1
793
794
gtoken_t t;
795
switch (tok) {
796
case TOK_OP_MUL_ASSIGN: t = TOK_OP_MUL; break;
797
case TOK_OP_DIV_ASSIGN: t = TOK_OP_DIV; break;
798
case TOK_OP_REM_ASSIGN: t = TOK_OP_REM; break;
799
case TOK_OP_ADD_ASSIGN: t = TOK_OP_ADD; break;
800
case TOK_OP_SUB_ASSIGN: t = TOK_OP_SUB; break;
801
case TOK_OP_SHIFT_LEFT_ASSIGN: t = TOK_OP_SHIFT_LEFT; break;
802
case TOK_OP_SHIFT_RIGHT_ASSIGN: t = TOK_OP_SHIFT_RIGHT; break;
803
case TOK_OP_BIT_AND_ASSIGN: t = TOK_OP_BIT_AND; break;
804
case TOK_OP_BIT_OR_ASSIGN: t = TOK_OP_BIT_OR; break;
805
case TOK_OP_BIT_XOR_ASSIGN: t = TOK_OP_BIT_XOR; break;
806
807
// should never reach this point
808
default: assert(0); break;
809
}
810
811
// duplicate node is mandatory here, otherwise the deallocator will try to free memory occopied by the same node twice
812
rnode = gnode_binary_expr_create(t, gnode_duplicate(lnode, true), rnode);
813
tok = TOK_OP_ASSIGN;
814
815
// its an assignment expression so switch the order
816
return gnode_binary_expr_create(tok, lnode, rnode);
817
}
818
819
static void init_grammer_rules (void) {
820
static bool created = false;
821
if (created) return;
822
created = true;
823
824
// rules is a static variable initialized to 0
825
// so we automatically have all members initialized to UNUSED
826
827
rules[TOK_OP_OPEN_PARENTHESIS] = RULE(PREC_CALL, parse_parentheses_expression, parse_postfix_call);
828
rules[TOK_OP_OPEN_SQUAREBRACKET] = RULE(PREC_CALL, parse_list_expression, parse_postfix_subscript);
829
rules[TOK_OP_DOT] = RULE(PREC_CALL, parse_literal_expression, parse_postfix_access);
830
831
rules[TOK_OP_OPEN_CURLYBRACE] = PREFIX(PREC_LOWEST, parse_function_expression);
832
rules[TOK_KEY_FUNC] = PREFIX(PREC_LOWEST, parse_function_expression);
833
834
rules[TOK_IDENTIFIER] = PREFIX(PREC_LOWEST, parse_identifier_expression);
835
rules[TOK_STRING] = PREFIX(PREC_LOWEST, parse_literal_expression);
836
rules[TOK_NUMBER] = PREFIX(PREC_LOWEST, parse_literal_expression);
837
838
rules[TOK_KEY_UNDEFINED] = PREFIX(PREC_LOWEST, parse_keyword_expression);
839
rules[TOK_KEY_CURRARGS] = PREFIX(PREC_LOWEST, parse_keyword_expression);
840
rules[TOK_KEY_CURRFUNC] = PREFIX(PREC_LOWEST, parse_keyword_expression);
841
rules[TOK_KEY_SUPER] = PREFIX(PREC_LOWEST, parse_keyword_expression);
842
rules[TOK_KEY_FILE] = PREFIX(PREC_LOWEST, parse_file_expression);
843
rules[TOK_KEY_NULL] = PREFIX(PREC_LOWEST, parse_keyword_expression);
844
rules[TOK_KEY_TRUE] = PREFIX(PREC_LOWEST, parse_keyword_expression);
845
rules[TOK_KEY_FALSE] = PREFIX(PREC_LOWEST, parse_keyword_expression);
846
847
rules[TOK_OP_SHIFT_LEFT] = INFIX_OPERATOR(PREC_SHIFT, "<<");
848
rules[TOK_OP_SHIFT_RIGHT] = INFIX_OPERATOR(PREC_SHIFT, ">>");
849
850
rules[TOK_OP_MUL] = INFIX_OPERATOR(PREC_FACTOR, "*");
851
rules[TOK_OP_DIV] = INFIX_OPERATOR(PREC_FACTOR, "/");
852
rules[TOK_OP_REM] = INFIX_OPERATOR(PREC_FACTOR, "%");
853
rules[TOK_OP_BIT_AND] = INFIX_OPERATOR(PREC_FACTOR, "&");
854
rules[TOK_OP_ADD] = OPERATOR(PREC_TERM, "+");
855
rules[TOK_OP_SUB] = OPERATOR(PREC_TERM, "-");
856
rules[TOK_OP_BIT_OR] = INFIX_OPERATOR(PREC_TERM, "|");
857
rules[TOK_OP_BIT_XOR] = INFIX_OPERATOR(PREC_TERM, "^");
858
rules[TOK_OP_BIT_NOT] = PREFIX_OPERATOR("~");
859
860
rules[TOK_OP_RANGE_EXCLUDED] = INFIX_OPERATOR(PREC_RANGE, "..<");
861
rules[TOK_OP_RANGE_INCLUDED] = INFIX_OPERATOR(PREC_RANGE, "...");
862
863
rules[TOK_KEY_ISA] = INFIX_OPERATOR(PREC_ISA, "isa");
864
rules[TOK_OP_LESS] = INFIX_OPERATOR(PREC_COMPARISON, "<");
865
rules[TOK_OP_LESS_EQUAL] = INFIX_OPERATOR(PREC_COMPARISON, "<=");
866
rules[TOK_OP_GREATER] = INFIX_OPERATOR(PREC_COMPARISON, ">");
867
rules[TOK_OP_GREATER_EQUAL] = INFIX_OPERATOR(PREC_COMPARISON, ">=");
868
rules[TOK_OP_ISEQUAL] = INFIX_OPERATOR(PREC_COMPARISON, "==");
869
rules[TOK_OP_ISNOTEQUAL] = INFIX_OPERATOR(PREC_COMPARISON, "!=");
870
rules[TOK_OP_ISIDENTICAL] = INFIX_OPERATOR(PREC_COMPARISON, "===");
871
rules[TOK_OP_ISNOTIDENTICAL] = INFIX_OPERATOR(PREC_COMPARISON, "!==");
872
rules[TOK_OP_PATTERN_MATCH] = INFIX_OPERATOR(PREC_COMPARISON, "~=");
873
874
rules[TOK_OP_AND] = INFIX_OPERATOR_RIGHT(PREC_LOGICAL_AND, "&&");
875
rules[TOK_OP_OR] = INFIX_OPERATOR_RIGHT(PREC_LOGICAL_OR, "||");
876
rules[TOK_OP_TERNARY] = INFIX(PREC_TERNARY, parse_ternary_expression);
877
878
rules[TOK_OP_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "=");
879
rules[TOK_OP_MUL_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "*=");
880
rules[TOK_OP_DIV_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "/=");
881
rules[TOK_OP_REM_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "%=");
882
rules[TOK_OP_ADD_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "+=");
883
rules[TOK_OP_SUB_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "-=");
884
rules[TOK_OP_SHIFT_LEFT_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "<<=");
885
rules[TOK_OP_SHIFT_RIGHT_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, ">>=");
886
rules[TOK_OP_BIT_AND_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "=&");
887
rules[TOK_OP_BIT_OR_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "|=");
888
rules[TOK_OP_BIT_XOR_ASSIGN] = INFIX_OPERATOR(PREC_ASSIGN, "^=");
889
890
rules[TOK_OP_NOT] = PREFIX_OPERATOR("!");
891
}
892
893
// MARK: - Declarations -
894
895
static gnode_t *parse_getter_setter (gravity_parser_t *parser) {
896
DEBUG_PARSER("parse_getter_setter");
897
DECLARE_LEXER;
898
899
gnode_t *getter = NULL;
900
gnode_t *setter = NULL;
901
gtoken_s token_block = gravity_lexer_token(lexer);
902
903
while (gravity_lexer_peek(lexer) != TOK_OP_CLOSED_CURLYBRACE) {
904
const char *identifier = parse_identifier(parser);
905
if (!identifier) goto parse_error;
906
907
bool is_getter = false;
908
gtoken_s token = gravity_lexer_token(lexer);
909
gnode_r *params = NULL;
910
911
// getter case: does not have explicit parameters (only implicit self)
912
if (strcmp(identifier, GETTER_FUNCTION_NAME) == 0) {
913
is_getter = true;
914
params = gnode_array_create(); // add implicit SELF param
915
gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SELF_PARAMETER_NAME), NULL, 0, NULL));
916
}
917
918
// setter case: could have explicit parameters (otherwise value is implicit)
919
if (strcmp(identifier, SETTER_FUNCTION_NAME) == 0) {
920
is_getter = false;
921
// check if parameters are explicit
922
if (gravity_lexer_peek(lexer) == TOK_OP_OPEN_PARENTHESIS) {
923
parse_required(parser, TOK_OP_OPEN_PARENTHESIS);
924
params = parse_optional_parameter_declaration(parser); // add implicit SELF
925
parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
926
} else {
927
params = gnode_array_create(); // add implicit SELF and VALUE params
928
gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SELF_PARAMETER_NAME), NULL, 0, NULL));
929
gnode_array_push(params, gnode_variable_create(NO_TOKEN, string_dup(SETTER_PARAMETER_NAME), NULL, 0, NULL));
930
}
931
}
932
mem_free(identifier);
933
934
// parse compound statement
935
PUSH_FUNCTION_DECLARATION();
936
gnode_compound_stmt_t *compound = (gnode_compound_stmt_t*)parse_compound_statement(parser);
937
POP_DECLARATION();
938
gnode_t *f = gnode_function_decl_create(token, NULL, 0, 0, params, compound);
939
940
// assign f to the right function
941
if (is_getter) getter = f; else setter = f;
942
}
943
944
gnode_r *functions = gnode_array_create();
945
gnode_array_push(functions, (getter) ? getter : NULL); // getter is at index 0
946
gnode_array_push(functions, (setter) ? setter : NULL); // setter is at index 1
947
948
// a compound node is used to capture getter and setter
949
return gnode_block_stat_create(NODE_COMPOUND_STAT, token_block, functions);
950
951
parse_error:
952
return NULL;
953
}
954
955
static gnode_t *parse_variable_declaration (gravity_parser_t *parser, bool isstatement, gtoken_t access_specifier, gtoken_t storage_specifier) {
956
DEBUG_PARSER("parse_variable_declaration");
957
DECLARE_LEXER;
958
959
gnode_r *decls = NULL;
960
gnode_t *decl = NULL;
961
gnode_t *expr = NULL;
962
const char *identifier = NULL;
963
const char *type_annotation = NULL;
964
gtoken_t type;
965
gtoken_t peek;
966
gtoken_s token, token2;
967
968
// access_specifier? storage_specifier? variable_declaration ';'
969
// variable_declaration: variable_declarator decl_item
970
// variable_declarator: 'const' | 'var'
971
// decl_item: (IDENTIFIER assignment?) (',' IDENTIFIER assignment?)*
972
// assignment
973
974
// sanity check on variable type
975
type = gravity_lexer_next(lexer);
976
if (!token_isvariable_declaration(type)) {
977
REPORT_ERROR(gravity_lexer_token(lexer), "VAR or CONST expected here but found %s.", token_name(type));
978
return NULL;
979
}
980
token = gravity_lexer_token(lexer);
981
982
// initialize node array
983
decls = gnode_array_create();
984
985
loop:
986
identifier = parse_identifier(parser);
987
if (!identifier) return NULL;
988
token2 = gravity_lexer_token(lexer);
989
990
// type annotation is optional so it can be NULL
991
type_annotation = parse_optional_type_annotation(parser);
992
DEBUG_PARSER("IDENTIFIER: %s %s", identifier, (type_annotation) ? type_annotation : "");
993
994
// check for optional assignment or getter/setter declaration (ONLY = is ALLOWED here!)
995
expr = NULL;
996
peek = gravity_lexer_peek(lexer);
997
if (token_isvariable_assignment(peek)) {
998
gravity_lexer_next(lexer); // consume ASSIGNMENT
999
expr = parse_expression(parser);
1000
} else if (peek == TOK_OP_OPEN_CURLYBRACE) {
1001
gravity_lexer_next(lexer); // consume TOK_OP_OPEN_CURLYBRACE
1002
expr = parse_getter_setter(parser);
1003
parse_required(parser, TOK_OP_CLOSED_CURLYBRACE);
1004
}
1005
1006
// sanity checks
1007
// 1. CONST must be followed by an assignment expression ?
1008
// 2. check if identifier is unique inside variable declarations
1009
1010
decl = gnode_variable_create(token2, identifier, type_annotation, access_specifier, expr);
1011
if (decl) gnode_array_push(decls, decl);
1012
1013
peek = gravity_lexer_peek(lexer);
1014
if (peek == TOK_OP_COMMA) {
1015
gravity_lexer_next(lexer); // consume TOK_OP_COMMA
1016
goto loop;
1017
}
1018
1019
// check and consume TOK_OP_SEMICOLON (ALWAYS required for assignments)
1020
// Aaron: I would keep it consistent, even if it's not strictly required.
1021
// Otherwise we end up with all of JavaScript's terrible ideas. ;-)
1022
// So I would require the semicolon at the end of any assignment statement.
1023
if (isstatement) parse_semicolon(parser);
1024
1025
return gnode_variable_decl_create(token, type, access_specifier, storage_specifier, decls);
1026
}
1027
1028
static gnode_t *parse_enum_declaration (gravity_parser_t *parser, gtoken_t access_specifier, gtoken_t storage_specifier) {
1029
DEBUG_PARSER("parse_enum_declaration");
1030
DECLARE_LEXER;
1031
1032
// enum is a bit different than the traditional C like enum statements
1033
// in Gravity enum can contains String, Integer, Boolean and Float BUT cannot be mixed
1034
// Integer case can also skip values and autoincrement will be applied
1035
// String and Float must have a default value
1036
// in any case default value must be unique (as identifiers)
1037
// this code will take care of parsing and syntax check for the above restrictions
1038
1039
// in order to simplify node struct all the sematic checks are performed here
1040
// even if it is not a best practice, I find a lot easier to perform all the checks
1041
// directly into the parser
1042
// parse_enum_declaration is the only reason why gravity_symboltable.h is included here
1043
1044
// checks are:
1045
// 1: unique internal identifiers
1046
// 2: unique internal values
1047
// 3: if not INT then a default value is mandatory
1048
// 4: all values must be literals
1049
1050
// 'enum' IDENTIFIER '{' enum_list '}' ';'
1051
// enum_list: enum_list_item (',' enum_list_item)*
1052
// enum_list_item: IDENTIFIER ('=' LITERAL)?
1053
1054
// NODE_ENUM_DECL
1055
1056
// optional scope already consumed
1057
gtoken_t type = gravity_lexer_next(lexer);
1058
gtoken_s token = gravity_lexer_token(lexer);
1059
assert(type == TOK_KEY_ENUM);
1060
1061
// parse IDENTIFIER
1062
const char *identifier = parse_identifier(parser);
1063
DEBUG_PARSER("parse_enum_declaration %s", identifier);
1064
1065
// check and consume TOK_OP_OPEN_CURLYBRACE
1066
parse_required(parser, TOK_OP_OPEN_CURLYBRACE);
1067
1068
symboltable_t *symtable = symboltable_create(true); // enum symbol table (symtable is OK because order is not important inside an enum)
1069
int64_t enum_autoint = 0; // autoincrement value (in case of INT enum)
1070
uint32_t enum_counter = 0; // enum internal counter (first value (if any) determines enum type)
1071
gliteral_t enum_type = LITERAL_INT; // enum type (default to int)
1072
1073
while (1) {
1074
// check for empty enum
1075
if (gravity_lexer_peek(lexer) == TOK_OP_CLOSED_CURLYBRACE) break;
1076
1077
// identifier is mandatory here
1078
const char *enum_id = NULL;
1079
gtoken_t peek = gravity_lexer_peek(lexer);
1080
gtoken_s enumid_token = NO_TOKEN;
1081
if (peek == TOK_IDENTIFIER) {
1082
enum_id = parse_identifier(parser);
1083
enumid_token = gravity_lexer_token(lexer);
1084
}
1085
if (!enum_id) {
1086
REPORT_ERROR(enumid_token, "Identifier expected here (found %s).", token_name(peek));
1087
}
1088
1089
// peek next that can be only = or , or }
1090
peek = gravity_lexer_peek(lexer);
1091
gtoken_s enum_token = gravity_lexer_token(lexer);
1092
if (!token_isvariable_assignment(peek) && (peek != TOK_OP_COMMA) && (peek != TOK_OP_CLOSED_CURLYBRACE)) {
1093
REPORT_ERROR(enum_token, "Token %s not allowed here.", token_name(peek));
1094
}
1095
1096
// check for assignment (ONLY = is ALLOWED here!)
1097
// assignment is optional ONLY for LITERAL_TYPE_INT case
1098
if ((!token_isvariable_assignment(peek)) && (enum_type != LITERAL_INT)) {
1099
REPORT_ERROR(enum_token, "A default value is expected here (found %s).", token_name(peek));
1100
}
1101
1102
// check for optional default value (optional only in LITERAL_INT case)
1103
gnode_base_t *enum_value = NULL;
1104
if (token_isvariable_assignment(peek)) {
1105
gravity_lexer_next(lexer); // consume ASSIGNMENT
1106
enum_value = (gnode_base_t *)parse_expression(parser);
1107
}
1108
1109
if (enum_value) {
1110
// make sure that value is a literal (or a unary expression like +num or -num)
1111
gnode_literal_expr_t *enum_literal = NULL;
1112
if (enum_value->base.tag == NODE_LITERAL_EXPR) {
1113
enum_literal = (gnode_literal_expr_t *)enum_value;
1114
} else if (enum_value->base.tag == NODE_UNARY_EXPR) {
1115
gnode_unary_expr_t *unary = (gnode_unary_expr_t *)enum_value;
1116
gnode_base_t *expr = (gnode_base_t *)unary->expr;
1117
1118
// sanity check on unary expression
1119
if (expr->base.tag != NODE_LITERAL_EXPR) {
1120
REPORT_ERROR(enum_token, "%s", "Literal value expected here.");
1121
continue;
1122
}
1123
1124
if ((unary->op != TOK_OP_SUB) && (unary->op != TOK_OP_ADD)) {
1125
REPORT_ERROR(enum_token, "%s", "Only + or - allowed in enum value definition.");
1126
continue;
1127
}
1128
1129
enum_literal = (gnode_literal_expr_t *)expr;
1130
if ((enum_literal->type != LITERAL_FLOAT) && (enum_literal->type != LITERAL_INT)) {
1131
REPORT_ERROR(enum_token, "%s", "A number is expected after a + or - unary expression in an enum definition.");
1132
continue;
1133
}
1134
1135
if (unary->op == TOK_OP_SUB) {
1136
if (enum_literal->type == LITERAL_FLOAT) enum_literal->value.d = -enum_literal->value.d;
1137
else if (enum_literal->type == LITERAL_INT) enum_literal->value.n64 = -enum_literal->value.n64;
1138
else assert(0); // should never reach this point
1139
}
1140
1141
} else {
1142
REPORT_ERROR(enum_token, "%s", "Literal value expected here.");
1143
continue;
1144
}
1145
1146
// first assignment (if any) determines enum type, otherwise default INT case is assumed
1147
if (enum_counter == 0) {
1148
if (enum_literal->type == LITERAL_STRING) enum_type = LITERAL_STRING;
1149
else if (enum_literal->type == LITERAL_FLOAT) enum_type = LITERAL_FLOAT;
1150
else if (enum_literal->type == LITERAL_BOOL) enum_type = LITERAL_BOOL;
1151
}
1152
1153
// check if literal value conforms to enum type
1154
if (enum_literal->type != enum_type) {
1155
REPORT_ERROR(enum_token, "%s", "Literal value of type %s expected here.", token_literal_name(enum_literal->type));
1156
}
1157
1158
// update enum_autoint value to next value
1159
if (enum_literal->type == LITERAL_INT) {
1160
enum_autoint = enum_literal->value.n64 + 1;
1161
}
1162
1163
} else {
1164
enum_value = (gnode_base_t *)gnode_literal_int_expr_create((gtoken_s)NO_TOKEN, enum_autoint);
1165
++enum_autoint;
1166
}
1167
1168
// update internal enum counter
1169
++enum_counter;
1170
1171
// enum identifier could be NULL due to an already reported error
1172
if (enum_id) {
1173
if (!symboltable_insert(symtable, enum_id, (void *)enum_value)) {
1174
REPORT_ERROR(enumid_token, "Identifier %s redeclared.", enum_id);
1175
gnode_free((gnode_t *)enum_value); // free value here because it has not beed saved into symbol table
1176
}
1177
mem_free(enum_id); // because key is duplicated inside internal hash table
1178
}
1179
1180
peek = gravity_lexer_peek(lexer);
1181
if (peek != TOK_OP_COMMA) break;
1182
1183
// consume TOK_OP_COMMA and continue loop
1184
gravity_lexer_next(lexer);
1185
}
1186
1187
// check and consume TOK_OP_CLOSED_CURLYBRACE
1188
parse_required(parser, TOK_OP_CLOSED_CURLYBRACE);
1189
1190
// consume semicolon
1191
parse_semicolon(parser);
1192
1193
// check for empty enum (not allowed)
1194
if (enum_counter == 0) {
1195
REPORT_ERROR(token, "Empty enum %s not allowed.", identifier);
1196
}
1197
1198
gnode_t *node = gnode_enum_decl_create(token, identifier, access_specifier, storage_specifier, symtable);
1199
if (IS_FUNCTION_ENCLOSED()) return local_store_declaration(identifier, access_specifier, storage_specifier, node);
1200
return node;
1201
}
1202
1203
static gnode_t *parse_module_declaration (gravity_parser_t *parser, gtoken_t access_specifier, gtoken_t storage_specifier) {
1204
DEBUG_PARSER("parse_module_declaration");
1205
1206
// 'module' IDENTIFIER '{' declaration_statement* '}' ';'
1207
1208
// optional scope already consumed
1209
DECLARE_LEXER;
1210
gtoken_t type = gravity_lexer_next(lexer);
1211
gtoken_s token = gravity_lexer_token(lexer);
1212
assert(type == TOK_KEY_MODULE);
1213
1214
// parse IDENTIFIER
1215
const char *identifier = parse_identifier(parser);
1216
DEBUG_PARSER("parse_module_declaration %s", identifier);
1217
1218
// parse optional curly brace
1219
bool curly_brace = parse_optional(parser, TOK_OP_OPEN_CURLYBRACE);
1220
1221
gnode_r *declarations = gnode_array_create();
1222
while (token_isdeclaration_statement(gravity_lexer_peek(lexer))) {
1223
gnode_t *decl = parse_declaration_statement(parser);
1224
if (decl) gnode_array_push(declarations, decl);
1225
}
1226
1227
// check and consume TOK_OP_CLOSED_CURLYBRACE
1228
if (curly_brace) parse_required(parser, TOK_OP_CLOSED_CURLYBRACE);
1229
1230
parse_semicolon(parser);
1231
1232
return gnode_module_decl_create(token, identifier, access_specifier, storage_specifier, declarations);
1233
}
1234
1235
static gnode_t *parse_event_declaration (gravity_parser_t *parser, gtoken_t access_specifier, gtoken_t storage_specifier) {
1236
#pragma unused(parser, access_specifier, storage_specifier)
1237
1238
// 'event' IDENTIFIER '(' parameter_declaration_clause? ')' ';'
1239
1240
// NODE_EVENT_DECL
1241
assert(0);
1242
return NULL;
1243
}
1244
1245
static gnode_t *parse_function_declaration (gravity_parser_t *parser, gtoken_t access_specifier, gtoken_t storage_specifier) {
1246
gnode_t *node = parse_function(parser, true, access_specifier, storage_specifier);
1247
1248
// convert a function declaration within another function to a local variable assignment
1249
// for example:
1250
//
1251
// func foo() {
1252
// func bar() {...}
1253
// }
1254
//
1255
// is converter to:
1256
//
1257
// func foo() {
1258
// var bar = func() {...}
1259
// }
1260
//
1261
// conversion is performed inside the parser
1262
// so next semantic checks can perform
1263
// identifier uniqueness checks
1264
1265
if (IS_FUNCTION_ENCLOSED()) return local_store_declaration(((gnode_function_decl_t *)node)->identifier, access_specifier, storage_specifier, node);
1266
return node;
1267
}
1268
1269
static gnode_t *parse_id (gravity_parser_t *parser) {
1270
DECLARE_LEXER;
1271
const char *identifier1 = NULL;
1272
const char *identifier2 = NULL;
1273
gtoken_t peek;
1274
gtoken_s token;
1275
1276
// IDENTIFIER | (IDENTIFIER)('.' IDENTIFIER)
1277
1278
DEBUG_PARSER("parse_id");
1279
1280
identifier1 = parse_identifier(parser);
1281
1282
token = gravity_lexer_token(lexer);
1283
peek = gravity_lexer_peek(lexer);
1284
if (peek == TOK_OP_DOT) {
1285
gravity_lexer_next(lexer); // consume TOK_OP_DOT
1286
identifier2 = parse_identifier(parser);
1287
}
1288
1289
DEBUG_PARSER("ID: %s %s", identifier1, (identifier2) ? identifier2 : "");
1290
return gnode_identifier_expr_create(token, identifier1, identifier2);
1291
}
1292
1293
static gnode_r *parse_protocols (gravity_parser_t *parser) {
1294
DECLARE_LEXER;
1295
gtoken_t peek;
1296
gnode_t *node = NULL;
1297
gnode_r *list = NULL;
1298
1299
// (id) (',' id)*
1300
1301
peek = gravity_lexer_peek(lexer);
1302
if (peek == TOK_OP_GREATER) return NULL; // just an empty protocols implementation statement
1303
1304
list = gnode_array_create();
1305
1306
loop:
1307
if (!token_isidentifier(peek)) goto abort;
1308
node = parse_id(parser);
1309
if (node) gnode_array_push(list, node);
1310
1311
peek = gravity_lexer_peek(lexer);
1312
if (peek == TOK_OP_COMMA) {
1313
gravity_lexer_next(lexer); // consume TOK_OP_COMMA
1314
goto loop;
1315
}
1316
1317
return list;
1318
1319
abort:
1320
if (list) gnode_array_free(list);
1321
return NULL;
1322
}
1323
1324
static gnode_t *parse_class_declaration (gravity_parser_t *parser, gtoken_t access_specifier, gtoken_t storage_specifier) {
1325
DEBUG_PARSER("parse_class_declaration");
1326
DECLARE_LEXER;
1327
1328
// access_specifier? storage_specifier? 'class' IDENTIFIER class_superclass? class_protocols? '{' declaration_statement* '}' ';'
1329
// class_superclass: (':') id
1330
// class_protocols: '<' (id) (',' id)* '>'
1331
1332
// optional scope already consumed (when here I am sure type is TOK_KEY_CLASS or TOK_KEY_STRUCT)
1333
gtoken_t type = gravity_lexer_next(lexer);
1334
gtoken_s token = gravity_lexer_token(lexer);
1335
bool is_struct = (type == TOK_KEY_STRUCT);
1336
1337
// parse IDENTIFIER
1338
const char *identifier = parse_identifier(parser);
1339
1340
// check for optional superclass
1341
gnode_t *super = NULL;
1342
gnode_r *protocols = NULL;
1343
gtoken_t peek = gravity_lexer_peek(lexer);
1344
if (peek == TOK_OP_COLON) {
1345
gravity_lexer_next(lexer); // consume TOK_OP_COLON
1346
super = parse_id(parser);
1347
}
1348
1349
// check for optional protocols (not supported in this version)
1350
peek = gravity_lexer_peek(lexer);
1351
if (peek == TOK_OP_LESS) {
1352
gravity_lexer_next(lexer); // consume '<'
1353
protocols = parse_protocols(parser);
1354
parse_required(parser, TOK_OP_GREATER); // consume '>'
1355
}
1356
1357
// check and consume TOK_OP_OPEN_CURLYBRACE
1358
parse_required(parser, TOK_OP_OPEN_CURLYBRACE);
1359
gnode_r *declarations = gnode_array_create();
1360
1361
// if class is declared inside another class then a hidden implicit privare "outer" instance var at index 0
1362
// is automatically added
1363
if (IS_CLASS_ENCLOSED()) {
1364
gnode_r *decls = gnode_array_create();
1365
gnode_t *outer_var = gnode_variable_create((gtoken_s)NO_TOKEN, string_dup(OUTER_IVAR_NAME), NULL, 0, NULL);
1366
gnode_array_push(decls, outer_var);
1367
1368
gnode_t *outer_decl = gnode_variable_decl_create((gtoken_s)NO_TOKEN, TOK_KEY_VAR, TOK_KEY_PRIVATE, 0, decls);
1369
gnode_array_push(declarations, outer_decl);
1370
}
1371
1372
PUSH_CLASS_DECLARATION();
1373
while (token_isdeclaration_statement(gravity_lexer_peek(lexer))) {
1374
gnode_t *decl = parse_declaration_statement(parser);
1375
if (decl) gnode_array_push(declarations, decl);
1376
}
1377
POP_DECLARATION();
1378
1379
// check and consume TOK_OP_CLOSED_CURLYBRACE
1380
parse_required(parser, TOK_OP_CLOSED_CURLYBRACE);
1381
1382
// to check
1383
parse_semicolon(parser);
1384
1385
gnode_t *node = gnode_class_decl_create(token, identifier, access_specifier, storage_specifier, super, protocols, declarations, is_struct);
1386
if (IS_FUNCTION_ENCLOSED()) return local_store_declaration(identifier, access_specifier, storage_specifier, node);
1387
return node;
1388
}
1389
1390
static gnode_r *parse_optional_parameter_declaration (gravity_parser_t *parser) {
1391
DEBUG_PARSER("parse_parameter_declaration");
1392
DECLARE_LEXER;
1393
1394
gtoken_s token = NO_TOKEN;
1395
gnode_t *node = NULL;
1396
const char *identifier = NULL;
1397
const char *type_annotation = NULL;
1398
1399
// (IDENTIFIER type_annotation?) (',' type_annotation)*
1400
// type_annotation: ':' identifier
1401
1402
gnode_r *params = gnode_array_create();
1403
assert(params);
1404
1405
// check if implicit self parameter must be added
1406
// was if (IS_CLASS_ENCLOSED()*/) { ... add SELF PARAMETER ...}
1407
// but we decided to ALWAYS pass SELF because it simplified cases
1408
// like c1().p1.p1.p1(1234);
1409
1410
// ALWAYS add an implicit SELF parameter
1411
// string_dup mandatory here because when the node will be freed
1412
// memory for the identifier will be deallocated
1413
node = gnode_variable_create(token, string_dup(SELF_PARAMETER_NAME), type_annotation, 0, NULL);
1414
if (node) gnode_array_push(params, node);
1415
1416
// parameter declaration clause is ALWAYS optional
1417
gtoken_t peek = gravity_lexer_peek(lexer);
1418
if (peek == TOK_OP_CLOSED_PARENTHESIS) return params;
1419
1420
// so there is at leat one explicit parameter
1421
loop:
1422
// initialize variables
1423
type_annotation = NULL;
1424
1425
// parse identifier
1426
identifier = parse_identifier(parser);
1427
token = gravity_lexer_token(lexer);
1428
1429
// parse optional type annotation
1430
type_annotation = parse_optional_type_annotation(parser);
1431
1432
// fill parameters array with the new node
1433
node = gnode_variable_create(token, identifier, type_annotation, 0, NULL);
1434
if (node) gnode_array_push(params, node);
1435
1436
// check for optional comma in order to decide
1437
// if the loop should continue or not
1438
peek = gravity_lexer_peek(lexer);
1439
if (peek == TOK_OP_COMMA) {
1440
gravity_lexer_next(lexer); // consume TOK_OP_COMMA
1441
goto loop;
1442
}
1443
1444
return params;
1445
}
1446
1447
// MARK: - UnitTest -
1448
1449
typedef enum {
1450
UNITTEST_NONE,
1451
UNITTEST_NAME,
1452
UNITTEST_ERROR,
1453
UNITTEST_RESULT,
1454
UNITTEST_ERROR_ROW,
1455
UNITTEST_ERROR_COL,
1456
UNITTEST_NOTE
1457
} unittest_t;
1458
1459
static unittest_t parse_unittest_identifier(const char *identifier) {
1460
if (string_cmp(identifier, "name") == 0) return UNITTEST_NAME;
1461
if (string_cmp(identifier, "note") == 0) return UNITTEST_NOTE;
1462
if (string_cmp(identifier, "error") == 0) return UNITTEST_ERROR;
1463
if (string_cmp(identifier, "error_row") == 0) return UNITTEST_ERROR_ROW;
1464
if (string_cmp(identifier, "error_col") == 0) return UNITTEST_ERROR_COL;
1465
if (string_cmp(identifier, "result") == 0) return UNITTEST_RESULT;
1466
1467
return UNITTEST_NONE;
1468
}
1469
1470
static gnode_t *parse_unittest_declaration(gravity_parser_t *parser) {
1471
DECLARE_LEXER;
1472
DEBUG_PARSER("parse_unittest_declaration");
1473
1474
// @unittest {
1475
// name: "Unit test name";
1476
// note: "Some notes here";
1477
// error: NONE, SYNTAX, RUNTIME, WARNING;
1478
// error_row: number;
1479
// error_col: number;
1480
// result: LITERAL;
1481
// '}' ';'?
1482
1483
gnode_literal_expr_t *name_node = NULL;
1484
gnode_literal_expr_t *note_node = NULL;
1485
gnode_identifier_expr_t *err_node = NULL;
1486
gnode_literal_expr_t *row_node = NULL;
1487
gnode_literal_expr_t *col_node = NULL;
1488
gnode_literal_expr_t *value_node = NULL;
1489
1490
parse_required(parser, TOK_OP_OPEN_CURLYBRACE);
1491
while (gravity_lexer_peek(lexer) != TOK_OP_CLOSED_CURLYBRACE) {
1492
const char *id = parse_identifier(parser);
1493
if (id == NULL) goto handle_error;
1494
parse_required(parser, TOK_OP_COLON);
1495
1496
unittest_t type = parse_unittest_identifier(id);
1497
mem_free(id);
1498
1499
if (type == UNITTEST_NAME) {
1500
name_node = (gnode_literal_expr_t *)parse_literal_expression(parser);
1501
if (name_node == NULL) goto handle_error;
1502
}
1503
else if (type == UNITTEST_NOTE) {
1504
note_node = (gnode_literal_expr_t *)parse_literal_expression(parser);
1505
if (note_node == NULL) goto handle_error;
1506
}
1507
else if (type == UNITTEST_ERROR) {
1508
err_node = (gnode_identifier_expr_t *)parse_identifier_expression(parser);
1509
if (err_node == NULL) goto handle_error;
1510
}
1511
else if (type == UNITTEST_ERROR_ROW) {
1512
row_node = (gnode_literal_expr_t *)parse_literal_expression(parser);
1513
if (row_node == NULL) goto handle_error;
1514
}
1515
else if (type == UNITTEST_ERROR_COL) {
1516
col_node = (gnode_literal_expr_t *)parse_literal_expression(parser);
1517
if (col_node == NULL) goto handle_error;
1518
}
1519
else if (type == UNITTEST_RESULT) {
1520
gtoken_t op = TOK_EOF;
1521
gtoken_t peek = gravity_lexer_peek(lexer);
1522
1523
// check if peek is a + or - sign
1524
if ((peek == TOK_OP_SUB) || (peek == TOK_OP_ADD))
1525
op = gravity_lexer_next(lexer);
1526
else if (peek == TOK_KEY_NULL) {
1527
// an expected return value can now be keyword NULL
1528
gravity_lexer_next(lexer); // consume NULL keyword
1529
value_node = NULL;
1530
goto handle_continue;
1531
}
1532
1533
value_node = (gnode_literal_expr_t *)parse_literal_expression(parser);
1534
if (value_node == NULL) goto handle_error;
1535
1536
// if a negative sign has been parsed then manually fix the literal expression (if it is a number)
1537
if (op == TOK_OP_SUB) {
1538
if (value_node->type == LITERAL_INT) value_node->value.n64 = -value_node->value.n64;
1539
else if (value_node->type == LITERAL_FLOAT) value_node->value.d = -value_node->value.d;
1540
}
1541
}
1542
else {
1543
REPORT_ERROR(gravity_lexer_token(lexer), "Unknown token found in @unittest declaration.");
1544
goto handle_error;
1545
}
1546
1547
handle_continue:
1548
parse_semicolon(parser);
1549
}
1550
1551
parse_required(parser, TOK_OP_CLOSED_CURLYBRACE);
1552
parse_semicolon(parser);
1553
1554
// decode unit array and report error/unittest
1555
// unit test name max length is 1024
1556
const char *description = NULL;
1557
const char *note = NULL;
1558
char buffer[1024];
1559
char buffer2[1024];
1560
error_type_t expected_error = GRAVITY_ERROR_NONE;
1561
gravity_value_t expected_value = VALUE_FROM_NULL;
1562
int32_t expected_nrow = -1;
1563
int32_t expected_ncol = -1;
1564
1565
// unittest name should be a literal string
1566
if ((name_node) && (name_node->type == LITERAL_STRING)) {
1567
// no more C strings in AST so we need a static buffer
1568
snprintf(buffer, sizeof(buffer), "%.*s", name_node->len, name_node->value.str);
1569
description = buffer;
1570
}
1571
1572
// note (optional) should be a literal string
1573
if ((note_node) && (note_node->type == LITERAL_STRING)) {
1574
// no more C strings in AST so we need a static buffer
1575
snprintf(buffer2, sizeof(buffer2), "%.*s", note_node->len, note_node->value.str);
1576
note = buffer2;
1577
}
1578
1579
// decode expected error: NONE, SYNTAX, SEMANTIC, RUNTIME, WARNING
1580
if (err_node) {
1581
if (string_cmp(err_node->value, "NONE") == 0)
1582
expected_error = GRAVITY_ERROR_NONE;
1583
else if (string_cmp(err_node->value, "SYNTAX") == 0)
1584
expected_error = GRAVITY_ERROR_SYNTAX;
1585
else if (string_cmp(err_node->value, "SEMANTIC") == 0)
1586
expected_error = GRAVITY_ERROR_SEMANTIC;
1587
else if (string_cmp(err_node->value, "RUNTIME") == 0)
1588
expected_error = GRAVITY_ERROR_RUNTIME;
1589
else if (string_cmp(err_node->value, "WARNING") == 0)
1590
expected_error = GRAVITY_WARNING;
1591
}
1592
1593
// decode error line/col
1594
if ((row_node) && (row_node->type == LITERAL_INT)) {
1595
expected_nrow = (int32_t)row_node->value.n64;
1596
}
1597
if ((col_node) && (col_node->type == LITERAL_INT)) {
1598
expected_ncol = (int32_t)col_node->value.n64;
1599
}
1600
1601
// decode unittest expected result
1602
if (value_node) {
1603
if (value_node->type == LITERAL_STRING)
1604
expected_value = VALUE_FROM_CSTRING(NULL, value_node->value.str);
1605
else if (value_node->type == LITERAL_INT)
1606
expected_value = VALUE_FROM_INT((gravity_int_t)value_node->value.n64);
1607
else if (value_node->type == LITERAL_FLOAT)
1608
expected_value = VALUE_FROM_FLOAT((gravity_float_t)value_node->value.d);
1609
else if (value_node->type == LITERAL_BOOL)
1610
expected_value = (value_node->value.n64) ? VALUE_FROM_TRUE : VALUE_FROM_FALSE;
1611
}
1612
1613
// report unittest to delegate
1614
if ((parser->delegate) && (parser->delegate->unittest_callback)) {
1615
gravity_unittest_callback unittest_cb = parser->delegate->unittest_callback;
1616
unittest_cb(expected_error, description, note, expected_value, expected_nrow, expected_ncol, parser->delegate->xdata);
1617
} else {
1618
// it was unit test responsability to free expected_value but if no unit test delegate is set I should take care of it
1619
gravity_value_free(NULL, expected_value);
1620
}
1621
1622
// free temp nodes
1623
if (name_node) gnode_free((gnode_t*)name_node);
1624
if (note_node) gnode_free((gnode_t*)note_node);
1625
if (err_node) gnode_free((gnode_t*)err_node);
1626
if (row_node) gnode_free((gnode_t*)row_node);
1627
if (col_node) gnode_free((gnode_t*)col_node);
1628
if (value_node) gnode_free((gnode_t*)value_node);
1629
1630
// always return NULL
1631
handle_error:
1632
return NULL;
1633
}
1634
1635
// MARK: - Statements -
1636
1637
static gnode_t *parse_label_statement (gravity_parser_t *parser) {
1638
DEBUG_PARSER("parse_label_statement");
1639
1640
// 'case' expression ':' statement
1641
// 'default' ':' statement
1642
1643
DECLARE_LEXER;
1644
gtoken_t type = gravity_lexer_next(lexer);
1645
gtoken_s token = gravity_lexer_token(lexer);
1646
assert((type == TOK_KEY_CASE) || (type == TOK_KEY_DEFAULT));
1647
1648
// case specific expression
1649
gnode_t *expr = NULL;
1650
if (type == TOK_KEY_CASE) {
1651
expr = parse_expression(parser);
1652
}
1653
1654
// common part
1655
parse_required(parser, TOK_OP_COLON);
1656
gnode_t *stmt = parse_statement(parser);
1657
1658
return gnode_label_stat_create(token, expr, stmt);
1659
}
1660
1661
static gnode_t *parse_flow_statement (gravity_parser_t *parser) {
1662
DEBUG_PARSER("parse_flow_statement");
1663
1664
// 'if' '(' expression ')' statement ('else' statement)?
1665
// 'switch' '(' expression ')' statement
1666
1667
DECLARE_LEXER;
1668
gtoken_t type = gravity_lexer_next(lexer);
1669
gtoken_s token = gravity_lexer_token(lexer);
1670
assert((type == TOK_KEY_IF) || (type == TOK_KEY_SWITCH));
1671
1672
// check optional TOK_OP_OPEN_PARENTHESIS
1673
bool is_parenthesize = parse_optional(parser, TOK_OP_OPEN_PARENTHESIS);
1674
1675
// parse common expression
1676
gnode_t *cond = parse_expression(parser);
1677
1678
// check and consume TOK_OP_CLOSED_PARENTHESIS
1679
if (is_parenthesize) parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
1680
1681
// parse common statement
1682
gnode_t *stmt1 = parse_statement(parser);
1683
gnode_t *stmt2 = NULL;
1684
if ((type == TOK_KEY_IF) && (gravity_lexer_peek(lexer) == TOK_KEY_ELSE)) {
1685
gravity_lexer_next(lexer); // consume TOK_KEY_ELSE
1686
stmt2 = parse_statement(parser);
1687
}
1688
1689
return gnode_flow_stat_create(token, cond, stmt1, stmt2);
1690
}
1691
1692
static gnode_t *parse_loop_statement (gravity_parser_t *parser) {
1693
DEBUG_PARSER("parse_loop_statement");
1694
1695
// 'while' '(' expression ')' statement
1696
// 'repeat' statement 'while' '(' expression ')' ';'
1697
// 'for' '(' condition 'in' expression ')' statement
1698
1699
DECLARE_LEXER;
1700
gnode_t *cond = NULL;
1701
gnode_t *stmt = NULL;
1702
gnode_t *expr = NULL;
1703
1704
gtoken_t type = gravity_lexer_next(lexer);
1705
gtoken_s token = gravity_lexer_token(lexer);
1706
assert((type == TOK_KEY_WHILE) || (type == TOK_KEY_REPEAT) || (type == TOK_KEY_FOR));
1707
1708
// 'while' '(' expression ')' statement
1709
if (type == TOK_KEY_WHILE) {
1710
// check optional TOK_OP_OPEN_PARENTHESIS
1711
bool is_parenthesize = parse_optional(parser, TOK_OP_OPEN_PARENTHESIS);
1712
1713
// parse while condition
1714
cond = parse_expression(parser);
1715
1716
// check and consume TOK_OP_CLOSED_PARENTHESIS
1717
if (is_parenthesize) parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
1718
1719
// parse while statement
1720
stmt = parse_statement(parser);
1721
1722
goto return_node;
1723
}
1724
1725
// 'repeat' statement 'while' '(' expression ')' ';'
1726
if (type == TOK_KEY_REPEAT) {
1727
// parse repeat statement
1728
stmt = parse_statement(parser);
1729
1730
// check and consume TOK_KEY_WHILE
1731
parse_required(parser, TOK_KEY_WHILE);
1732
1733
// check optional TOK_OP_OPEN_PARENTHESIS
1734
bool is_parenthesize = parse_optional(parser, TOK_OP_OPEN_PARENTHESIS);
1735
1736
// parse while expression
1737
expr = parse_expression(parser);
1738
1739
// check and consume TOK_OP_CLOSED_PARENTHESIS
1740
if (is_parenthesize) parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
1741
1742
// semicolon
1743
parse_semicolon(parser);
1744
1745
goto return_node;
1746
}
1747
1748
// 'for' '(' condition 'in' expression ')' statement
1749
if (type == TOK_KEY_FOR) {
1750
// check optional TOK_OP_OPEN_PARENTHESIS
1751
bool is_parenthesize = parse_optional(parser, TOK_OP_OPEN_PARENTHESIS);
1752
1753
// parse condition (means parse variable declaration or expression)
1754
if (token_isvariable_declaration(gravity_lexer_peek(lexer))) {
1755
cond = parse_variable_declaration(parser, false, 0, 0);
1756
} else {
1757
cond = parse_expression(parser);
1758
}
1759
1760
// check and consume TOK_KEY_IN
1761
parse_required(parser, TOK_KEY_IN);
1762
1763
// parse expression
1764
expr = parse_expression(parser);
1765
1766
// check and consume TOK_OP_CLOSED_PARENTHESIS
1767
if (is_parenthesize) parse_required(parser, TOK_OP_CLOSED_PARENTHESIS);
1768
1769
// parse for statement
1770
stmt = parse_statement(parser);
1771
}
1772
1773
return_node:
1774
return gnode_loop_stat_create(token, cond, stmt, expr);
1775
}
1776
1777
static gnode_t *parse_jump_statement (gravity_parser_t *parser) {
1778
DEBUG_PARSER("parse_jump_statement");
1779
1780
// 'break' ';'
1781
// 'continue' ';'
1782
// 'return' expression? ';'
1783
1784
DECLARE_LEXER;
1785
gtoken_t type = gravity_lexer_next(lexer);
1786
gtoken_s token = gravity_lexer_token(lexer);
1787
assert((type == TOK_KEY_BREAK) || (type == TOK_KEY_CONTINUE) || (type == TOK_KEY_RETURN));
1788
1789
gnode_t *expr = NULL;
1790
if ((type == TOK_KEY_RETURN) && (gravity_lexer_peek(lexer) != TOK_OP_SEMICOLON)) {
1791
expr = parse_expression(parser);
1792
}
1793
1794
parse_semicolon(parser);
1795
return gnode_jump_stat_create(token, expr);
1796
}
1797
1798
static gnode_t *parse_compound_statement (gravity_parser_t *parser) {
1799
DEBUG_PARSER("parse_compound_statement");
1800
1801
// '{' (statement+)? '}'
1802
1803
// check and consume TOK_OP_OPEN_CURLYBRACE
1804
parse_required(parser, TOK_OP_OPEN_CURLYBRACE);
1805
1806
DECLARE_LEXER;
1807
gtoken_s token = gravity_lexer_token(lexer);
1808
gnode_r *stmts = gnode_array_create();
1809
while (token_isstatement(gravity_lexer_peek(lexer))) {
1810
gnode_t *node = parse_statement(parser);
1811
if (node) gnode_array_push(stmts, node);
1812
}
1813
1814
// check and consume TOK_OP_CLOSED_CURLYBRACE
1815
parse_required(parser, TOK_OP_CLOSED_CURLYBRACE);
1816
1817
return gnode_block_stat_create(NODE_COMPOUND_STAT, token, stmts);
1818
}
1819
1820
static gnode_t *parse_empty_statement (gravity_parser_t *parser) {
1821
DEBUG_PARSER("parse_empty_statement");
1822
1823
// ;
1824
1825
DECLARE_LEXER;
1826
gravity_lexer_next(lexer);
1827
gtoken_s token = gravity_lexer_token(lexer);
1828
return gnode_empty_stat_create(token);
1829
}
1830
1831
static gnode_t *parse_declaration_statement (gravity_parser_t *parser) {
1832
DEBUG_PARSER("parse_declaration_statement");
1833
1834
DECLARE_LEXER;
1835
gtoken_t peek = gravity_lexer_peek(lexer);
1836
gtoken_t access_specifier = 0; // 0 means no access specifier set
1837
gtoken_t storage_specifier = 0; // 0 means no storage specifier set
1838
1839
// check if an access specifier is set
1840
if (token_isaccess_specifier(peek)) {
1841
access_specifier = gravity_lexer_next(lexer);
1842
peek = gravity_lexer_peek(lexer);
1843
}
1844
1845
// check if a storage specifier is set
1846
if (token_isstorage_specifier(peek)) {
1847
storage_specifier = gravity_lexer_next(lexer);
1848
peek = gravity_lexer_peek(lexer);
1849
}
1850
1851
// it is a syntax error to specify an access or storage specifier followed by an empty declaration
1852
if ((peek == TOK_OP_SEMICOLON) && ((access_specifier) || (storage_specifier))) {
1853
REPORT_ERROR(gravity_lexer_token(lexer), "%s", "Access or storage specifier cannot be used here.");
1854
}
1855
1856
switch (peek) {
1857
case TOK_KEY_FUNC: return parse_function_declaration(parser, access_specifier, storage_specifier);
1858
case TOK_KEY_ENUM: return parse_enum_declaration(parser, access_specifier, storage_specifier);
1859
case TOK_KEY_MODULE: return parse_module_declaration(parser, access_specifier, storage_specifier);
1860
case TOK_KEY_EVENT: return parse_event_declaration(parser, access_specifier, storage_specifier);
1861
case TOK_KEY_CLASS:
1862
case TOK_KEY_STRUCT: return parse_class_declaration(parser, access_specifier, storage_specifier);
1863
case TOK_OP_SEMICOLON: return parse_empty_statement(parser);
1864
case TOK_KEY_VAR:
1865
case TOK_KEY_CONST: return parse_variable_declaration(parser, true, access_specifier, storage_specifier);
1866
default: REPORT_ERROR(gravity_lexer_token(lexer), "Unrecognized token %s.", token_name(peek)); return NULL;
1867
}
1868
1869
// should never reach this point
1870
assert(0);
1871
return NULL;
1872
}
1873
1874
static gnode_t *parse_import_statement (gravity_parser_t *parser) {
1875
DEBUG_PARSER("parse_import_statement");
1876
1877
DECLARE_LEXER;
1878
1879
// parse import keyword
1880
gravity_lexer_next(lexer);
1881
1882
// process filename (can be an identifier or a literal string)
1883
// identifier to import modules ?
1884
// only literals are supported in this version
1885
gtoken_t type;
1886
gtoken_s token;
1887
const char *module_name;
1888
gravity_lexer_t *newlexer;
1889
1890
loop:
1891
newlexer = NULL;
1892
type = gravity_lexer_next(lexer);
1893
token = gravity_lexer_token(lexer);
1894
1895
// check if it is a string token
1896
if (type != TOK_STRING) {
1897
REPORT_ERROR(token, "Expected file name but found %s.", token_name(type));
1898
return NULL;
1899
}
1900
1901
// check pre-requisites
1902
if ((!parser->delegate) || (!parser->delegate->loadfile_callback)) {
1903
REPORT_ERROR(gravity_lexer_token(lexer), "%s", "Unable to load file because no loadfile callback registered in delegate.");
1904
return NULL;
1905
}
1906
1907
// parse string
1908
module_name = cstring_from_token(parser, token);
1909
size_t size = 0;
1910
uint32_t fileid = 0;
1911
1912
// module_name is a filename and it is used by lexer to store filename into tokens
1913
// tokens are then stored inside AST nodes in order to locate errors into source code
1914
// AST can live a lot longer than both lexer and parser so we need a way to persistent
1915
// store these chuncks of memory
1916
const char *source = parser->delegate->loadfile_callback(module_name, &size, &fileid, parser->delegate->xdata);
1917
if (source) newlexer = gravity_lexer_create(source, size, fileid, false);
1918
1919
if (newlexer) {
1920
// push new lexer into lexer stack
1921
marray_push(gravity_lexer_t*, *parser->lexer, newlexer);
1922
} else {
1923
REPORT_ERROR(token, "Unable to load file %s.", module_name);
1924
}
1925
1926
// check for optional comma
1927
if (gravity_lexer_peek(lexer) == TOK_OP_COMMA) {
1928
gravity_lexer_next(lexer); // consume TOK_OP_COMMA
1929
goto loop;
1930
}
1931
1932
// parse semicolon
1933
parse_semicolon(parser);
1934
1935
return NULL;
1936
}
1937
1938
static gnode_t *parse_macro_statement (gravity_parser_t *parser) {
1939
DEBUG_PARSER("parse_macro_statement");
1940
DECLARE_LEXER;
1941
1942
// consume special # symbol
1943
gtoken_t type = gravity_lexer_next(lexer);
1944
assert(type == TOK_MACRO);
1945
1946
// macro has its own parser because I don't want to mess standard syntax
1947
const char *macroid = parse_identifier(parser);
1948
if (macroid == NULL) goto handle_error;
1949
1950
// check #unittest macro
1951
bool is_unittest = (string_cmp(macroid, "unittest") == 0);
1952
mem_free(macroid);
1953
1954
if (is_unittest) {
1955
return parse_unittest_declaration(parser);
1956
}
1957
1958
handle_error:
1959
REPORT_WARNING(gravity_lexer_token(lexer), "%s", "Unknown macro token. Declaration will be ignored.");
1960
return NULL;
1961
}
1962
1963
static gnode_t *parse_special_statement (gravity_parser_t *parser) {
1964
DEBUG_PARSER("parse_special_statement");
1965
DECLARE_LEXER;
1966
1967
// consume special @ symbol
1968
gtoken_t type = gravity_lexer_next(lexer);
1969
assert(type == TOK_SPECIAL);
1970
1971
// special is really special so it has its own parser
1972
// because I don't want to mess standard syntax
1973
const char *specialid = parse_identifier(parser);
1974
if (specialid == NULL) goto handle_error;
1975
1976
handle_error:
1977
REPORT_WARNING(gravity_lexer_token(lexer), "%s", "Unknown special token. Declaration will be ignored.");
1978
return NULL;
1979
}
1980
1981
static gnode_t *parse_expression_statement (gravity_parser_t *parser) {
1982
DEBUG_PARSER("parse_expression_statement");
1983
1984
gnode_t *expr = parse_expression(parser);
1985
parse_semicolon(parser);
1986
return expr;
1987
}
1988
1989
static gnode_t *parse_statement (gravity_parser_t *parser) {
1990
DEBUG_PARSER("parse_statement");
1991
1992
// label_statement
1993
// flow_statement
1994
// loop_statement
1995
// jump_statement
1996
// compound_statement
1997
// declaration_statement
1998
// empty_statement
1999
// import_statement
2000
// expression_statement (default)
2001
2002
DECLARE_LEXER;
2003
gtoken_t token = gravity_lexer_peek(lexer);
2004
if (token_iserror(token)) return parse_error(parser);
2005
2006
if (token_islabel_statement(token)) return parse_label_statement(parser);
2007
else if (token_isflow_statement(token)) return parse_flow_statement(parser);
2008
else if (token_isloop_statement(token)) return parse_loop_statement(parser);
2009
else if (token_isjump_statement(token)) return parse_jump_statement(parser);
2010
else if (token_iscompound_statement(token)) return parse_compound_statement(parser);
2011
else if (token_isdeclaration_statement(token)) return parse_declaration_statement(parser);
2012
else if (token_isempty_statement(token)) return parse_empty_statement(parser);
2013
else if (token_isimport_statement(token)) return parse_import_statement(parser);
2014
else if (token_isspecial_statement(token)) return parse_special_statement(parser);
2015
else if (token_ismacro(token)) return parse_macro_statement(parser);
2016
2017
return parse_expression_statement(parser); // DEFAULT
2018
}
2019
2020
// MARK: - Internal functions -
2021
2022
static void parser_register_core_classes (gravity_parser_t *parser) {
2023
const char **list = NULL;
2024
uint32_t n = gravity_core_identifiers(&list);
2025
if (!n) return;
2026
2027
// for each core identifier create a dummy extern variable node
2028
gnode_r *decls = gnode_array_create();
2029
for (uint32_t i=0; i<n; ++i) {
2030
const char *identifier = list[i];
2031
gnode_t *node = gnode_variable_create(NO_TOKEN, string_dup(identifier), NULL, 0, NULL);
2032
gnode_array_push(decls, node);
2033
}
2034
2035
// register a variable declaration node in global statements
2036
gnode_t *node = gnode_variable_decl_create(NO_TOKEN, TOK_KEY_VAR, 0, TOK_KEY_EXTERN, decls);;
2037
gnode_array_push(parser->statements, (gnode_t *)node);
2038
}
2039
2040
static uint32_t parser_run (gravity_parser_t *parser) {
2041
DEBUG_PARSER("=== BEGIN PARSING ===");
2042
2043
// register core classes as extern globals
2044
parser_register_core_classes(parser);
2045
2046
nanotime_t t1 = nanotime();
2047
do {
2048
while (gravity_lexer_peek(CURRENT_LEXER)) {
2049
gnode_t *node = parse_statement(parser);
2050
if (node) gnode_array_push(parser->statements, node);
2051
}
2052
2053
// since it is a stack of lexers then check if it is a real EOF
2054
gravity_lexer_t *lexer = CURRENT_LEXER;
2055
gravity_lexer_free(lexer);
2056
marray_pop(*parser->lexer);
2057
2058
} while (marray_size(*parser->lexer));
2059
nanotime_t t2 = nanotime();
2060
parser->time = millitime(t1, t2);
2061
2062
DEBUG_PARSER("=== END PARSING ===\n");
2063
2064
return parser->nerrors;
2065
}
2066
2067
static void parser_cleanup (gravity_parser_t *parser) {
2068
// in case of error (so AST is not returned)
2069
// then cleanup internal nodes
2070
gnode_t *node= gnode_block_stat_create(NODE_LIST_STAT, (gtoken_s)NO_TOKEN, parser->statements);
2071
gnode_free(node);
2072
}
2073
2074
static void parser_appendcode (const char *source, gravity_parser_t *parser) {
2075
if (source == NULL) return;
2076
2077
size_t len = strlen(source);
2078
if (len <= 0) return;
2079
2080
// build a new lexer based on source code to prepend
2081
gravity_lexer_t *lexer1 = gravity_lexer_create(source, len, 0, true);
2082
if (!lexer1) return;
2083
2084
// pop current lexer
2085
gravity_lexer_t *lexer2 = POP_LEXER;
2086
2087
// swap lexer2 with lexer1
2088
marray_push(gravity_lexer_t*, *parser->lexer, lexer1);
2089
marray_push(gravity_lexer_t*, *parser->lexer, lexer2);
2090
}
2091
2092
// MARK: - Public functions -
2093
2094
gravity_parser_t *gravity_parser_create (const char *source, size_t len, uint32_t fileid, bool is_static) {
2095
init_grammer_rules();
2096
2097
gravity_parser_t *parser = mem_alloc(sizeof(gravity_parser_t));
2098
if (!parser) return NULL;
2099
2100
gravity_lexer_t *lexer = gravity_lexer_create(source, len, fileid, is_static);
2101
if (!lexer) goto abort_init;
2102
2103
parser->lexer = mem_alloc(sizeof(lexer_r));
2104
marray_init(*parser->lexer);
2105
marray_push(gravity_lexer_t*, *parser->lexer, lexer);
2106
2107
parser->statements = gnode_array_create();
2108
if (!parser->statements) goto abort_init;
2109
2110
marray_init(parser->declarations);
2111
2112
parser->last_error_lineno = UINT32_MAX;
2113
return parser;
2114
2115
abort_init:
2116
gravity_parser_free(parser);
2117
return NULL;
2118
}
2119
2120
gnode_t *gravity_parser_run (gravity_parser_t *parser, gravity_delegate_t *delegate) {
2121
parser->delegate = delegate;
2122
gravity_lexer_setdelegate(CURRENT_LEXER, delegate);
2123
2124
// check if some user code needs to be prepended
2125
if ((delegate) && (delegate->precode_callback))
2126
parser_appendcode(delegate->precode_callback(delegate->xdata), parser);
2127
2128
// if there are syntax errors then just returns
2129
if (parser_run(parser) > 0) {
2130
parser_cleanup (parser);
2131
return NULL;
2132
}
2133
2134
// if there are some open declarations then there should be an error somewhere
2135
if (marray_size(parser->declarations) > 0) return NULL;
2136
2137
// return ast
2138
return gnode_block_stat_create(NODE_LIST_STAT, (gtoken_s)NO_TOKEN, parser->statements);
2139
}
2140
2141
void gravity_parser_free (gravity_parser_t *parser) {
2142
// free memory for stack of lexers
2143
if (parser->lexer) {
2144
size_t _len = marray_size(*parser->lexer);
2145
for (size_t i=0; i<_len; ++i) {
2146
gravity_lexer_t *lexer = (gravity_lexer_t *)marray_get(*parser->lexer, i);
2147
gravity_lexer_free(lexer);
2148
}
2149
marray_destroy(*parser->lexer);
2150
mem_free(parser->lexer);
2151
}
2152
2153
marray_destroy(parser->declarations);
2154
// parser->statements is returned from gravity_parser_run
2155
// and must be deallocated using gnode_free
2156
2157
mem_free(parser);
2158
}
2159
2160