1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
#include <stdbool.h>
#include <string.h>
#include "../ast.h"
#include "../stdlib/util.h"
#include "context.h"
#include "errors.h"
#include "expressions.h"
#include "suffixes.h"
#include "utils.h"
ast_e match_binary_operator(const char **pos) {
switch (**pos) {
case '+': {
*pos += 1;
return match(pos, "+") ? Concat : Plus;
}
case '-': {
*pos += 1;
if ((*pos)[0] != ' ' && (*pos)[-2] == ' ') // looks like `fn -5`
return Unknown;
return Minus;
}
case '*': *pos += 1; return Multiply;
case '/': *pos += 1; return Divide;
case '^': *pos += 1; return Power;
case '<': {
*pos += 1;
if (match(pos, "=")) return LessThanOrEquals; // "<="
else if (match(pos, ">")) return Compare; // "<>"
else if (match(pos, "<")) {
if (match(pos, "<")) return UnsignedLeftShift; // "<<<"
return LeftShift; // "<<"
} else return LessThan;
}
case '>': {
*pos += 1;
if (match(pos, "=")) return GreaterThanOrEquals; // ">="
if (match(pos, ">")) {
if (match(pos, ">")) return UnsignedRightShift; // ">>>"
return RightShift; // ">>"
}
return GreaterThan;
}
default: {
if (match(pos, "!=")) return NotEquals;
else if (match(pos, "==") && **pos != '=') return Equals;
else if (match_word(pos, "and")) return And;
else if (match_word(pos, "or")) return Or;
else if (match_word(pos, "xor")) return Xor;
else if (match_word(pos, "mod1")) return Mod1;
else if (match_word(pos, "mod")) return Mod;
else if (match_word(pos, "_min_")) return Min;
else if (match_word(pos, "_max_")) return Max;
else return Unknown;
}
}
}
ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightness) {
ast_t *lhs = optional(ctx, &pos, parse_term);
if (!lhs) return NULL;
int64_t starting_line = get_line_number(ctx->file, pos);
int64_t starting_indent = get_indent(ctx, pos);
spaces(&pos);
for (ast_e op; (op = match_binary_operator(&pos)) != Unknown && op_tightness[op] >= min_tightness; spaces(&pos)) {
ast_t *key = NULL;
if (op == Min || op == Max) {
key = NewAST(ctx->file, pos, pos, Var, .name = (op == Min ? "_min_" : "_max_"));
for (bool progress = true; progress;) {
ast_t *new_term;
progress =
(false || (new_term = parse_index_suffix(ctx, key))
|| (new_term = parse_method_call_suffix(ctx, key)) || (new_term = parse_field_suffix(ctx, key))
|| (new_term = parse_fncall_suffix(ctx, key)) || (new_term = parse_optional_suffix(ctx, key))
|| (new_term = parse_non_optional_suffix(ctx, key)));
if (progress) key = new_term;
}
if (key && key->tag == Var) key = NULL;
else if (key) pos = key->end;
}
whitespace(ctx, &pos);
if (get_line_number(ctx->file, pos) != starting_line && get_indent(ctx, pos) < starting_indent)
parser_err(ctx, pos, eol(pos), "I expected this line to be at least as indented than the line above it");
ast_t *rhs = parse_infix_expr(ctx, pos, op_tightness[op] + 1);
if (!rhs) break;
pos = rhs->end;
if (op == Min) {
return NewAST(ctx->file, lhs->start, rhs->end, Min, .lhs = lhs, .rhs = rhs, .key = key);
} else if (op == Max) {
return NewAST(ctx->file, lhs->start, rhs->end, Max, .lhs = lhs, .rhs = rhs, .key = key);
} else {
lhs = new (ast_t, .file = ctx->file, .start = lhs->start, .end = rhs->end, .tag = op,
.__data.Plus.lhs = lhs, .__data.Plus.rhs = rhs);
}
}
return lhs;
}
|