Skip to content

Commit 0a80344

Browse files
committed
Add C-style for loops
1 parent 078429a commit 0a80344

5 files changed

Lines changed: 157 additions & 0 deletions

File tree

docs/bext.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ Some targets like `gas-aarch64-darwin` have a different calling convention for v
5656
this is needed to make the compiler use the correct calling convention. \
5757
the syntax is `__variadic__(function_name, number_of_fixed_args);`
5858
59+
## C-style for loops
60+
```c
61+
main() {
62+
for (auto i = 2; i < 5; i++) {
63+
printf("%lld^2 = %lld\n", i, i*i);
64+
}
65+
66+
for(;;) printf("infinite loop\n");
67+
}
68+
```
69+
70+
Very useful for iterating over a range of numbers.
71+
5972
<!--
6073
TODO: hex-literals and C++ style comments are currently considered deviations
6174
and not extensions, thus disabled in historical mode, which is a bug.

src/b.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,64 @@ pub unsafe fn compile_statement(l: *mut Lexer, c: *mut Compiler) -> Option<()> {
789789
push_opcode(Op::Label {label: out_label}, (*l).loc, c);
790790
Some(())
791791
}
792+
Token::For => {
793+
scope_push(&mut (*c).vars);
794+
get_and_expect_token(l, Token::OParen)?;
795+
796+
let cond_label = allocate_label_index(c);
797+
let iter_label = allocate_label_index(c);
798+
let body_label = allocate_label_index(c);
799+
let out_label = allocate_label_index(c);
800+
801+
let saved_point = (*l).parse_point;
802+
lexer::get_token(l)?;
803+
804+
if (*l).token == Token::Auto {
805+
get_and_expect_token(l, Token::ID)?;
806+
let name = arena::strdup(&mut (*c).arena, (*l).string);
807+
let index = allocate_auto_var(&mut (*c).auto_vars_ator);
808+
declare_var(c, name, (*l).loc, Storage::Auto {index})?;
809+
get_and_expect_token(l, Token::Eq)?;
810+
let loc = (*l).loc;
811+
let (arg, _) = compile_expression(l, c)?;
812+
push_opcode(Op::AutoAssign {index, arg}, loc, c);
813+
get_and_expect_token(l, Token::SemiColon)?;
814+
} else if (*l).token != Token::SemiColon {
815+
(*l).parse_point = saved_point;
816+
compile_expression(l, c)?;
817+
get_and_expect_token(l, Token::SemiColon)?;
818+
}
819+
820+
push_opcode(Op::Label {label: cond_label}, (*l).loc, c);
821+
let saved_point = (*l).parse_point;
822+
lexer::get_token(l);
823+
if (*l).token != Token::SemiColon {
824+
(*l).parse_point = saved_point;
825+
let (arg, _) = compile_expression(l, c)?;
826+
push_opcode(Op::JmpIfNotLabel{label: out_label, arg}, (*l).loc, c);
827+
get_and_expect_token(l, Token::SemiColon)?;
828+
}
829+
push_opcode(Op::JmpLabel{label: body_label}, (*l).loc, c);
830+
831+
push_opcode(Op::Label {label: iter_label}, (*l).loc, c);
832+
let saved_point = (*l).parse_point;
833+
lexer::get_token(l);
834+
if (*l).token != Token::CParen {
835+
(*l).parse_point = saved_point;
836+
compile_expression(l, c)?;
837+
get_and_expect_token(l, Token::CParen)?;
838+
}
839+
push_opcode(Op::JmpLabel{label: cond_label}, (*l).loc, c);
840+
841+
842+
push_opcode(Op::Label {label: body_label}, (*l).loc, c);
843+
compile_statement(l, c)?;
844+
push_opcode(Op::JmpLabel{label: iter_label}, (*l).loc, c);
845+
push_opcode(Op::Label {label: out_label}, (*l).loc, c);
846+
847+
scope_pop(&mut (*c).vars);
848+
Some(())
849+
}
792850
Token::Return => {
793851
get_and_expect_tokens(l, &[Token::SemiColon, Token::OParen])?;
794852
if (*l).token == Token::SemiColon {

src/lexer.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ pub enum Token {
8888
If,
8989
Else,
9090
While,
91+
For,
9192
Switch,
9293
Goto,
9394
Return,
@@ -153,6 +154,7 @@ pub unsafe fn display_token(token: Token) -> *const c_char {
153154
Token::If => c!("keyword `if`"),
154155
Token::Else => c!("keyword `else`"),
155156
Token::While => c!("keyword `while`"),
157+
Token::For => c!("keyword `for`"),
156158
Token::Switch => c!("keyword `switch`"),
157159
Token::Goto => c!("keyword `goto`"),
158160
Token::Return => c!("keyword `return`"),
@@ -259,6 +261,7 @@ const KEYWORDS: *const [(*const c_char, Token)] = &[
259261
(c!("if"), Token::If),
260262
(c!("else"), Token::Else),
261263
(c!("while"), Token::While),
264+
(c!("for"), Token::For),
262265
(c!("switch"), Token::Switch),
263266
(c!("goto"), Token::Goto),
264267
(c!("return"), Token::Return),

tests.json

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,5 +2147,61 @@
21472147
"expected_stdout": "",
21482148
"state": "Disabled",
21492149
"comment": "Doesn't make sense for this target"
2150+
},
2151+
{
2152+
"case": "for_loop",
2153+
"target": "ilasm-mono",
2154+
"expected_stdout": "",
2155+
"state": "Enabled",
2156+
"comment": "Failed to build on record"
2157+
},
2158+
{
2159+
"case": "for_loop",
2160+
"target": "gas-aarch64-linux",
2161+
"expected_stdout": "i: 0\ni: 1\ni: 2\ni outside: 69\nj: 3\nj: 5\nj: 7\nx: 2\nx: 4\nx: 8\ny: 69\ny: 70\ny: 71\nz: 100\nz: 101\nz: 102\noh no!\n",
2162+
"state": "Enabled",
2163+
"comment": ""
2164+
},
2165+
{
2166+
"case": "for_loop",
2167+
"target": "gas-aarch64-darwin",
2168+
"expected_stdout": "i: 0\ni: 1\ni: 2\ni outside: 69\nj: 3\nj: 5\nj: 7\nx: 2\nx: 4\nx: 8\ny: 69\ny: 70\ny: 71\nz: 100\nz: 101\nz: 102\noh no!\n",
2169+
"state": "Enabled",
2170+
"comment": ""
2171+
},
2172+
{
2173+
"case": "for_loop",
2174+
"target": "gas-x86_64-linux",
2175+
"expected_stdout": "i: 0\ni: 1\ni: 2\ni outside: 69\nj: 3\nj: 5\nj: 7\nx: 2\nx: 4\nx: 8\ny: 69\ny: 70\ny: 71\nz: 100\nz: 101\nz: 102\noh no!\n",
2176+
"state": "Enabled",
2177+
"comment": ""
2178+
},
2179+
{
2180+
"case": "for_loop",
2181+
"target": "gas-x86_64-windows",
2182+
"expected_stdout": "i: 0\r\ni: 1\r\ni: 2\r\ni outside: 69\r\nj: 3\r\nj: 5\r\nj: 7\r\nx: 2\r\nx: 4\r\nx: 8\r\ny: 69\r\ny: 70\r\ny: 71\r\nz: 100\r\nz: 101\r\nz: 102\r\noh no!\r\n",
2183+
"state": "Enabled",
2184+
"comment": ""
2185+
},
2186+
{
2187+
"case": "for_loop",
2188+
"target": "gas-x86_64-darwin",
2189+
"expected_stdout": "i: 0\ni: 1\ni: 2\ni outside: 69\nj: 3\nj: 5\nj: 7\nx: 2\nx: 4\nx: 8\ny: 69\ny: 70\ny: 71\nz: 100\nz: 101\nz: 102\noh no!\n",
2190+
"state": "Enabled",
2191+
"comment": ""
2192+
},
2193+
{
2194+
"case": "for_loop",
2195+
"target": "6502-posix",
2196+
"expected_stdout": "i: 0\r\ni: 1\r\ni: 2\r\ni outside: 69\r\nj: 3\r\nj: 5\r\nj: 7\r\nx: 2\r\nx: 4\r\nx: 8\r\ny: 69\r\ny: 70\r\ny: 71\r\nz: 100\r\nz: 101\r\nz: 102\r\noh no!\r\n",
2197+
"state": "Enabled",
2198+
"comment": ""
2199+
},
2200+
{
2201+
"case": "for_loop",
2202+
"target": "uxn",
2203+
"expected_stdout": "i: 0\ni: 1\ni: 2\ni outside: 69\nj: 3\nj: 5\nj: 7\nx: 2\nx: 4\nx: 8\ny: 69\ny: 70\ny: 71\nz: 100\nz: 101\nz: 102\noh no!\n",
2204+
"state": "Enabled",
2205+
"comment": ""
21502206
}
21512207
]

tests/for_loop.b

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
main() {
2+
auto i; i = 69;
3+
for (auto i = 0; i < 3; i++) printf("i: %lld\n", i);
4+
printf("i outside: %lld\n", i);
5+
6+
auto j;
7+
for (j = 3; j < 9; j += 2) printf("j: %lld\n", j);
8+
9+
auto x; x = 2;
10+
for (;x < 10; x *= 2) printf("x: %lld\n", x);
11+
12+
auto y; y = 69;
13+
for (;y <= 71;) printf("y: %lld\n", y++);
14+
15+
auto z; z = 100;
16+
for (;;z++) {
17+
printf("z: %lld\n", z);
18+
if (z >= 102) goto out1;
19+
}
20+
out1:
21+
22+
for(;;) {
23+
printf("oh no!\n");
24+
goto out2;
25+
}
26+
out2:
27+
}

0 commit comments

Comments
 (0)