blob: 4069da8e0a1764c3157d1395f37755bd8f5b35e2 (
plain)
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
|
Metaprogramming
===============
See also "compile_time_execution.txt".
Use cases:
* Centralizing definitions. E.g. an error message might have:
- an identifier
- a severity level
- a message
* "Functions" that can implicitly use variables or return from the outer function.
* "Functions" that create variables or statements
* "Functions" that create types or enum values
* etc.
Solutions:
* Pre-processor
* Typed pre-processor
* Code generation
* Code verification (write the code manually, but have an external tool to verify it)
* Compile-time execution
* Compile-time verification (write the code manually, but have the compiler verify it)
* Dynamic dispatch / runtime interpretation
(this might actually be faster than code generation due to better I-cache utilization!)
* ...?
Desirable properties:
* Should be type safe
* It would be nice if it can run in a loop to generate repeated code.
Syntax idea 1:
func print_operator(arena Output out, ref Operator op)
{
switch op.kind {
%each %Operator %{
case %(Operator.enum_ident):
print_string(%(Operator.name))
%if %(Operator.arity == 2) %{
print_expr(operator.operand1)
print_expr(operator.operand2)
%} %else %{
print_expr(operator.operand1)
%}
%}
}
}
Syntax idea 2:
func print_operator(arena Output out, ref Operator op)
{
switch op.kind {
case %(Operator.enum_ident):
print_string(%(Operator.name))
%if %(Operator.arity == 2) %{
print_expr(operator.operand1)
print_expr(operator.operand2)
%} %else %{
print_expr(operator.operand1)
%}
}
}
Syntax idea 3:
func print_operator(arena Output out, ref Operator op)
{
switch op.kind {
case %enum_ident:
print_string(%OPERATOR_NAME(%enum_ident))
%if OPERATOR_ARITY(%enum_ident) == 2 %{
print_expr(operator.operand1)
print_expr(operator.operand2)
%} %else %{
print_expr(operator.operand1)
%}
}
}
Syntax idea 4 (dynamic). Best solution?
type OpInfo = struct {
string name
int arity
}
data OpInfo[OperatorKind] opinfos = [
.plus = ("plus", 2)
.minus = ("minus", 2)
...
]
func print_operator(arena Output out, ref Operator op)
{
OpInfo opinfo = opinfo[op.kind]
print_string(opinfo.name)
if opinfo.arity == 2 {
print_expr(operator.operand1)
print_expr(operator.operand2)
} else {
print_expr(operator.operand1)
}
}
|