Skip to content

Commit b641940

Browse files
committed
Add if-else and logical negation + fix a scope bug with for
1 parent 9191214 commit b641940

11 files changed

Lines changed: 399 additions & 67 deletions

File tree

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,33 +76,38 @@ The language supports the following compiler intrinsics:
7676

7777
The following language features are supported:
7878

79-
- `if` statements, written like so:
79+
- `if`, `else if`, `else` statements, written like so:
8080

8181
```av
8282
if (condition) {
8383
84+
} else if (condition) {
85+
86+
} else {
87+
8488
}
8589
```
8690

87-
- `while` loops:
91+
- `for` loops:
8892

8993
```
90-
while (condition) {
94+
for (init; condition; incr) {
9195
9296
}
9397
```
9498

95-
- `for` loops:
99+
- `while` loops:
96100

97101
```
98-
for (init; condition; incr) {
102+
while (condition) {
99103
100104
}
101105
```
102106

103107
where `condition` is true if it is not equal to `0`, and `init` and `incr` can be any statements.
104108

105109
- Bitwise AND and OR (written like `a | b` and `a & b`).
110+
- Logical negation (written like `~x`), **which is different from C** that would write `!x`.
106111
- Comparasions (<, >, <=, >=, etc.)
107112
- Arithmetic (+, -, *, %, etc.)
108113

spec/7.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ Same story here.
1717

1818
## Alloca
1919

20-
`alloca(bytes)` allocates ((bytes + 16) / 16) * 16 bytes of memory on the stack.
20+
`alloca(bytes)` allocates ((bytes + 15) / 16) * 16 bytes of memory on the stack.

spec/9.md

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Quality of life
22

3-
Adding `!` and `else` would *really* improve quality of life, but let's do that later.
3+
Adding `~` (for logical negation) and `else` would *really* improve quality of life, but let's do that later.
44

55
## For loops
66

@@ -28,3 +28,115 @@ Although, **this** does seem to work:
2828
}
2929
}
3030
```
31+
32+
## Logical negation
33+
34+
Simply do the following:
35+
36+
```asm
37+
cmp rax, 0
38+
sete al
39+
movzx eax, al
40+
```
41+
42+
Note that I've made an unconventional choice and used the symbol `~` instead of `!` for logical negation.
43+
44+
## Else
45+
46+
```av
47+
if (cond) {
48+
<body1>
49+
} else {
50+
<body2>
51+
}
52+
```
53+
54+
Currently, plain `if` works like this:
55+
56+
```asm
57+
mov rax, <cond>
58+
cmp rax, 0
59+
jz .after
60+
<body1>
61+
.after:
62+
```
63+
64+
To add `else` to this, we can do this:
65+
66+
```asm
67+
mov rax, <cond>
68+
cmp rax, 0
69+
jz .body
70+
<body1>
71+
jmp .after
72+
.body:
73+
<body2>
74+
.after:
75+
```
76+
77+
As for `else if`, let's say we had:
78+
79+
```av
80+
if (cond1) {
81+
<body1>
82+
} else if (cond2) {
83+
<body2>
84+
} else if (cond3) {
85+
<body3>
86+
} else {
87+
<body4>
88+
}
89+
```
90+
91+
We'd write this as:
92+
93+
```asm
94+
mov rax, <cond1>
95+
cmp rax, 0
96+
jz .cond2
97+
<body1>
98+
jmp .after
99+
.cond2:
100+
mov rax, <cond2>
101+
cmp rax, 0
102+
jz .cond3
103+
<body2>
104+
jmp .after
105+
.cond3:
106+
mov rax, <cond3>
107+
cmp rax, 0
108+
jz .cond4
109+
<body3>
110+
jmp .after
111+
.cond4:
112+
<body4>
113+
.after:
114+
```
115+
116+
So the best way to do this would probably be to let the desugaring layer group together `if`, `else if`, ..., `else` under some node called IfElseBlock, and then just spit out the above codegen.
117+
118+
## Correction regarding for loops
119+
120+
I was very close last time, but the actual correct lowering should be:
121+
122+
```av
123+
{
124+
init;
125+
while (cond) {
126+
{
127+
...
128+
}
129+
incr;
130+
}
131+
}
132+
```
133+
134+
Since, otherwise, loops like this would be problematic:
135+
136+
```av
137+
for (int32 i = 0; i < 5; i = i + 1) {
138+
int32 i = 0;
139+
}
140+
```
141+
142+
But this is an easy fix.

src/ast.cpp

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ std::string av::to_string(av::NodeType type) {
1111
"Identifier", "Return", "Equal", "Less", "Greater",
1212
"LessEqual", "GreaterEqual", "ShiftLeft", "ShiftRight",
1313
"Plus", "Minus", "UnaryMinus", "Multiply", "Div", "Modulo",
14-
"BitwiseAnd", "BitwiseOr", "If", "While"
14+
"BitwiseAnd", "BitwiseOr", "LogicalNeg", "If", "ElseIf", "Else", "While",
15+
"IfElseBlock"
1516
});
1617
return a[int(type)];
1718
}
@@ -50,74 +51,77 @@ std::ostream &av::operator<<(std::ostream &os, const std::vector<T> &v) {
5051
return os << "]";
5152
}
5253

53-
std::ostream &av::Node::print(std::ostream &os, int dep) const {
54+
std::ostream &av::print(const Node *u, std::ostream &os, int dep) {
55+
if (u == nullptr) {
56+
return os;
57+
}
5458
std::string tab(dep, ' ');
55-
os << tab << av::to_string(type) << '\n';
56-
switch (type) {
59+
os << tab << av::to_string(u->type) << '\n';
60+
switch (u->type) {
5761
case block: {
58-
for (Node *&i : ((Block *)this)->stmts) {
59-
i->print(os, dep + 1);
62+
for (Node *&i : ((Block *)u)->stmts) {
63+
print(i, os, dep + 1);
6064
}
6165
} break;
6266
case functionDecl: {
63-
FunctionDecl *t = (FunctionDecl *)this;
67+
FunctionDecl *t = (FunctionDecl *)u;
6468
os << tab << "Name: " << t->Name << '\n';
6569
os << tab << "ReturnType: " << t->ReturnType << '\n';
6670
os << tab << "ParamTypes: " << t->ParamTypes << '\n';
6771
} break;
6872
case functionBody: {
69-
FunctionBody *t = (FunctionBody *)this;
73+
FunctionBody *t = (FunctionBody *)u;
7074
os << tab << "Name: " << t->Name << '\n';
7175
os << tab << "ReturnType: " << t->ReturnType << '\n';
7276
os << tab << "ParamTypes: " << t->ParamTypes << '\n';
7377
os << tab << "Params: " << t->Params << '\n';
7478
os << tab << "Body:\n";
75-
t->Body->print(os, dep + 1);
79+
print(t->Body, os, dep + 1);
7680
} break;
7781
case variableDecl: {
78-
VariableDecl *t = (VariableDecl *)this;
82+
VariableDecl *t = (VariableDecl *)u;
7983
os << tab << "Name: " << t->Name << '\n';
8084
os << tab << "Type: " << t->VariableType << '\n';
8185
} break;
8286
case assign: {
83-
Assign *t = (Assign *)this;
87+
Assign *t = (Assign *)u;
8488
os << tab << "To:\n";
85-
t->To->print(os, dep + 1);
89+
print(t->To, os, dep + 1);
8690
os << tab << "Value:\n";
87-
t->Value->print(os, dep + 1);
91+
print(t->Value, os, dep + 1);
8892
} break;
8993
case addressOf: {
90-
os << tab << "Name: " << ((AddressOf *)this)->Name << '\n';
94+
os << tab << "Name: " << ((AddressOf *)u)->Name << '\n';
9195
} break;
9296
case dereference: {
93-
os << tab << "Name: " << ((Dereference *)this)->Name << '\n';
97+
os << tab << "Name: " << ((Dereference *)u)->Name << '\n';
9498
} break;
9599
case functionCall: {
96-
FunctionCall *t = (FunctionCall *)this;
100+
FunctionCall *t = (FunctionCall *)u;
97101
os << tab << "Name: " << t->Name << '\n';
98102
os << tab << "Params:\n";
99103
for (Node *&i : t->Params) {
100-
i->print(os, dep + 1);
104+
print(i, os, dep + 1);
101105
}
102106
} break;
103107
case identifier: {
104-
os << tab << "Name: " << ((Identifier *)this)->Name << '\n';
108+
os << tab << "Name: " << ((Identifier *)u)->Name << '\n';
105109
} break;
106110
case int8Literal: {
107-
os << tab << "Value: " << int(((Int8Literal *)this)->Value) << '\n';
111+
os << tab << "Value: " << int(((Int8Literal *)u)->Value) << '\n';
108112
} break;
109113
case int16Literal: {
110-
os << tab << "Value: " << ((Int16Literal *)this)->Value << '\n';
114+
os << tab << "Value: " << ((Int16Literal *)u)->Value << '\n';
111115
} break;
112116
case int32Literal: {
113-
os << tab << "Value: " << ((Int32Literal *)this)->Value << '\n';
117+
os << tab << "Value: " << ((Int32Literal *)u)->Value << '\n';
114118
} break;
115119
case int64Literal: {
116-
os << tab << "Value: " << ((Int64Literal *)this)->Value << '\n';
120+
os << tab << "Value: " << ((Int64Literal *)u)->Value << '\n';
117121
} break;
118122
case returnNode: {
119123
os << tab << "Value:\n";
120-
((Return *)this)->Value->print(os, dep + 1);
124+
print(((Return *)u)->Value, os, dep + 1);
121125
} break;
122126
case equal:
123127
case less:
@@ -134,32 +138,55 @@ std::ostream &av::Node::print(std::ostream &os, int dep) const {
134138
case bitwiseAnd:
135139
case bitwiseOr: {
136140
os << tab << "Lhs:\n";
137-
((Binary *)this)->Lhs->print(os, dep + 1);
141+
print(((Binary *)u)->Lhs, os, dep + 1);
138142
os << tab << "Rhs:\n";
139-
((Binary *)this)->Rhs->print(os, dep + 1);
143+
print(((Binary *)u)->Rhs, os, dep + 1);
140144
} break;
141145
case unaryMinus: {
142146
os << tab << "To:\n";
143-
((UnaryMinus *)this)->To->print(os, dep + 1);
147+
print(((UnaryMinus *)u)->To, os, dep + 1);
148+
} break;
149+
case logicalNegate: {
150+
os << tab << "To:\n";
151+
print(((LogicalNegate *)u)->To, os, dep + 1);
144152
} break;
145153
case ifNode: {
146-
If *t = (If *)this;
154+
If *t = (If *)u;
155+
os << tab << "Cond:\n";
156+
print(t->Cond, os, dep + 1);
157+
os << tab << "Body:\n";
158+
print(t->Body, os, dep + 1);
159+
} break;
160+
case elseIf: {
161+
ElseIf *t = (ElseIf *)u;
147162
os << tab << "Cond:\n";
148-
t->Cond->print(os, dep + 1);
163+
print(t->Cond, os, dep + 1);
164+
os << tab << "Body:\n";
165+
print(t->Body, os, dep + 1);
166+
} break;
167+
case elseNode: {
168+
Else *t = (Else *)u;
149169
os << tab << "Body:\n";
150-
t->Body->print(os, dep + 1);
170+
print(t->Body, os, dep + 1);
171+
} break;
172+
case ifElseBlock: {
173+
IfElseBlock *t = (IfElseBlock *)u;
174+
os << tab << "Conds:\n";
175+
for (Node *&i : t->Conds) {
176+
print(i, os, dep + 1);
177+
}
151178
} break;
152179
case whileNode: {
153-
While *t = (While *)this;
180+
While *t = (While *)u;
154181
os << tab << "Cond:\n";
155-
t->Cond->print(os, dep + 1);
182+
print(t->Cond, os, dep + 1);
156183
os << tab << "Body:\n";
157-
t->Body->print(os, dep + 1);
184+
print(t->Body, os, dep + 1);
158185
} break;
159186
}
160187
return os;
161188
}
162189

163190
std::ostream &av::operator<<(std::ostream &os, const av::Node &t) {
164-
return t.print(os);
191+
return print(&t, os);
165192
}

0 commit comments

Comments
 (0)