-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathevaluate.jou
151 lines (134 loc) · 5.79 KB
/
evaluate.jou
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import "stdlib/str.jou"
import "stdlib/mem.jou"
import "./ast.jou"
import "./errors_and_warnings.jou"
@public
def evaluate_array_length(expr: AstExpression*) -> int:
if expr->kind == AstExpressionKind.Int:
return expr->int_value
fail(expr->location, "cannot evaluate array length at compile time")
@public
def get_special_constant(name: byte*) -> int:
match name with strcmp:
case "WINDOWS":
return WINDOWS as int
case "MACOS":
return MACOS as int
case "NETBSD":
return NETBSD as int
case _:
return -1
def evaluate_condition(expr: AstExpression*) -> int: # 1=true 0=false -1=error
match expr->kind:
case AstExpressionKind.GetVariable:
return get_special_constant(expr->varname)
case AstExpressionKind.Bool:
return expr->bool_value as int
case AstExpressionKind.And:
match evaluate_condition(&expr->operands[0]):
case 1:
return evaluate_condition(&expr->operands[1])
case 0:
return 0 # left side false, don't evaluate right side
case -1:
return -1
case _:
assert False
case AstExpressionKind.Or:
match evaluate_condition(&expr->operands[0]):
case 1:
return 1 # left side true, don't evaluate right side
case 0:
return evaluate_condition(&expr->operands[1])
case -1:
return -1 # error
case _:
assert False
case AstExpressionKind.Not:
match evaluate_condition(&expr->operands[0]):
case 1:
return 0
case 0:
return 1
case -1:
return -1
case _:
assert False
case _:
return -1
# returns the statements to replace if statement with, as a pointer inside if_stmt
def choose_if_elif_branch(if_stmt: AstIfStatement*) -> AstBody*:
for i = 0; i < if_stmt->n_if_and_elifs; i++:
match evaluate_condition(&if_stmt->if_and_elifs[i].condition):
case -1:
# don't know how to evaluate it
return NULL
case 0:
# try the next elif or else
pass
case 1:
# condition is true, let's use this if or elif
return &if_stmt->if_and_elifs[i].body
case _:
assert False
return &if_stmt->else_body
# Replace body->statements[i] with zero or more statements from another body.
def replace(body: AstBody*, i: int, new: AstBody) -> None:
body->statements[i].free()
item_size = sizeof(body->statements[0])
body->statements = realloc(body->statements, (body->nstatements + new.nstatements) * item_size)
memmove(&body->statements[i + new.nstatements], &body->statements[i+1], (body->nstatements - (i+1)) * item_size)
memcpy(&body->statements[i], new.statements, new.nstatements * item_size)
free(new.statements)
body->nstatements--
body->nstatements += new.nstatements
def evaluate_if_statements_in_body(body: AstBody*, must_succeed: bool) -> None:
for i = 0; i < body->nstatements; i++:
match body->statements[i].kind:
case AstStatementKind.If:
ptr = choose_if_elif_branch(&body->statements[i].if_statement)
if ptr == NULL and must_succeed:
fail(body->statements[i].location, "cannot evaluate condition at compile time")
if ptr != NULL:
replacement = *ptr
*ptr = AstBody{} # avoid double-free
replace(body, i, replacement)
i-- # cancels i++ to do same index again, so that we handle nested if statements
case AstStatementKind.WhileLoop:
evaluate_if_statements_in_body(&body->statements[i].while_loop.body, False)
case AstStatementKind.ForLoop:
evaluate_if_statements_in_body(&body->statements[i].for_loop.body, False)
case AstStatementKind.Class:
evaluate_if_statements_in_body(body->statements[i].classdef.body, True)
case AstStatementKind.FunctionDef:
evaluate_if_statements_in_body(&body->statements[i].function.body, False)
case AstStatementKind.MethodDef:
evaluate_if_statements_in_body(&body->statements[i].method.body, False)
case (
AstStatementKind.ExpressionStatement
| AstStatementKind.Assert
| AstStatementKind.Pass
| AstStatementKind.Return
| AstStatementKind.Match
| AstStatementKind.Break
| AstStatementKind.Continue
| AstStatementKind.DeclareLocalVar
| AstStatementKind.Assign
| AstStatementKind.InPlaceAdd
| AstStatementKind.InPlaceSub
| AstStatementKind.InPlaceMul
| AstStatementKind.InPlaceDiv
| AstStatementKind.InPlaceMod
| AstStatementKind.FunctionDeclare
| AstStatementKind.Enum
| AstStatementKind.GlobalVariableDeclare
| AstStatementKind.GlobalVariableDef
| AstStatementKind.Import
| AstStatementKind.ClassField
| AstStatementKind.ClassUnion
):
# these statements cannot contain if statements, no need to recurse inside
pass
@public
def evaluate_compile_time_if_statements(file: AstFile*) -> None:
evaluate_if_statements_in_body(&file->body, True)