Skip to content

Commit 8cbdb38

Browse files
committed
Add support for for loops
This fixes #817. Changelog: added
1 parent 4f08289 commit 8cbdb38

File tree

9 files changed

+251
-3
lines changed

9 files changed

+251
-3
lines changed

ast/src/lexer.rs

+6
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ pub enum TokenKind {
115115
If,
116116
Implement,
117117
Import,
118+
In,
118119
Inline,
119120
Integer,
120121
Invalid,
@@ -272,6 +273,7 @@ impl TokenKind {
272273
TokenKind::Inline => "the 'inline' keyword",
273274
TokenKind::Copy => "the 'copy' keyword",
274275
TokenKind::Type => "the 'type' keyword",
276+
TokenKind::In => "the 'in' keyword",
275277
}
276278
}
277279
}
@@ -341,6 +343,7 @@ impl Token {
341343
| TokenKind::Inline
342344
| TokenKind::Copy
343345
| TokenKind::Type
346+
| TokenKind::In
344347
)
345348
}
346349

@@ -964,6 +967,7 @@ impl Lexer {
964967
"fn" => TokenKind::Fn,
965968
"if" => TokenKind::If,
966969
"or" => TokenKind::Or,
970+
"in" => TokenKind::In,
967971
_ => TokenKind::Identifier,
968972
},
969973
3 => match value.as_str() {
@@ -1359,6 +1363,7 @@ mod tests {
13591363
assert!(tok(TokenKind::Inline, "", 1..=1, 1..=1).is_keyword());
13601364
assert!(tok(TokenKind::Copy, "", 1..=1, 1..=1).is_keyword());
13611365
assert!(tok(TokenKind::Type, "", 1..=1, 1..=1).is_keyword());
1366+
assert!(tok(TokenKind::In, "", 1..=1, 1..=1).is_keyword());
13621367
}
13631368

13641369
#[test]
@@ -1966,6 +1971,7 @@ mod tests {
19661971
assert_token!("fn", Fn, "fn", 1..=1, 1..=2);
19671972
assert_token!("if", If, "if", 1..=1, 1..=2);
19681973
assert_token!("or", Or, "or", 1..=1, 1..=2);
1974+
assert_token!("in", In, "in", 1..=1, 1..=2);
19691975

19701976
assert_token!("and", And, "and", 1..=1, 1..=3);
19711977
assert_token!("for", For, "for", 1..=1, 1..=3);

ast/src/nodes.rs

+16
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@ pub enum Expression {
768768
Array(Box<Array>),
769769
Tuple(Box<Tuple>),
770770
Comment(Box<Comment>),
771+
For(Box<For>),
771772
}
772773

773774
impl Expression {
@@ -851,6 +852,7 @@ impl Node for Expression {
851852
Expression::Mut(ref typ) => typ.location(),
852853
Expression::Recover(ref typ) => typ.location(),
853854
Expression::Comment(ref n) => n.location(),
855+
Expression::For(ref n) => n.location(),
854856
}
855857
}
856858
}
@@ -1598,3 +1600,17 @@ impl Node for Module {
15981600
&self.location
15991601
}
16001602
}
1603+
1604+
#[derive(Debug, PartialEq, Eq)]
1605+
pub struct For {
1606+
pub pattern: Pattern,
1607+
pub iterator: Expression,
1608+
pub body: Expressions,
1609+
pub location: Location,
1610+
}
1611+
1612+
impl Node for For {
1613+
fn location(&self) -> &Location {
1614+
&self.location
1615+
}
1616+
}

ast/src/parser.rs

+54
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,7 @@ impl Parser {
17031703
TokenKind::Try => self.try_expression(start)?,
17041704
TokenKind::While => self.while_expression(start)?,
17051705
TokenKind::Let => self.define_variable(start)?,
1706+
TokenKind::For => self.for_expression(start)?,
17061707
TokenKind::Comment => Expression::Comment(self.comment(start)),
17071708
_ => {
17081709
error!(start.location, "'{}' can't be used here", start.value)
@@ -2527,6 +2528,7 @@ impl Parser {
25272528
| TokenKind::True
25282529
| TokenKind::Try
25292530
| TokenKind::While
2531+
| TokenKind::For
25302532
if same_line =>
25312533
{
25322534
let token = self.next();
@@ -2910,6 +2912,28 @@ impl Parser {
29102912
Ok(Expression::While(Box::new(While { condition, body, location })))
29112913
}
29122914

2915+
fn for_expression(
2916+
&mut self,
2917+
start: Token,
2918+
) -> Result<Expression, ParseError> {
2919+
let pattern = self.pattern()?;
2920+
2921+
self.expect(TokenKind::In)?;
2922+
2923+
let iter_tok = self.require()?;
2924+
let iter = self.expression(iter_tok)?;
2925+
let body_tok = self.expect(TokenKind::CurlyOpen)?;
2926+
let body = self.expressions(body_tok)?;
2927+
let location = Location::start_end(&start.location, body.location());
2928+
2929+
Ok(Expression::For(Box::new(For {
2930+
pattern,
2931+
iterator: iter,
2932+
body,
2933+
location,
2934+
})))
2935+
}
2936+
29132937
fn if_condition(&mut self) -> Result<IfCondition, ParseError> {
29142938
let cond_start = self.require()?;
29152939
let condition = self.expression(cond_start)?;
@@ -9720,6 +9744,36 @@ mod tests {
97209744
);
97219745
}
97229746

9747+
#[test]
9748+
fn test_for_expression() {
9749+
assert_eq!(
9750+
expr("for x in y { 20 }"),
9751+
Expression::For(Box::new(For {
9752+
pattern: Pattern::Identifier(Box::new(IdentifierPattern {
9753+
name: Identifier {
9754+
name: "x".to_string(),
9755+
location: cols(5, 5)
9756+
},
9757+
mutable: false,
9758+
value_type: None,
9759+
location: cols(5, 5)
9760+
})),
9761+
iterator: Expression::Identifier(Box::new(Identifier {
9762+
name: "y".to_string(),
9763+
location: cols(10, 10)
9764+
})),
9765+
body: Expressions {
9766+
values: vec![Expression::Int(Box::new(IntLiteral {
9767+
value: "20".to_string(),
9768+
location: cols(14, 15)
9769+
}))],
9770+
location: cols(12, 17)
9771+
},
9772+
location: cols(1, 17)
9773+
}))
9774+
);
9775+
}
9776+
97239777
#[test]
97249778
fn test_invalid_while_expression() {
97259779
assert_error_expr!("while 10 20 }", cols(10, 11));

compiler/src/format.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,7 @@ impl Document {
13301330
Expression::Try(n) => self.try_value(n),
13311331
Expression::Match(n) => self.match_value(n),
13321332
Expression::Scope(n) => self.scope(n),
1333+
Expression::For(n) => self.for_loop(n),
13331334
}
13341335
}
13351336

@@ -1568,6 +1569,30 @@ impl Document {
15681569
Node::Group(gid, group)
15691570
}
15701571

1572+
fn for_loop(&mut self, node: &nodes::For) -> Node {
1573+
let gid = self.new_group_id();
1574+
let pat = self.pattern(&node.pattern);
1575+
let col = self.expression(&node.iterator);
1576+
let header = vec![
1577+
Node::text("for"),
1578+
Node::SpaceOrLine,
1579+
Node::Indent(vec![pat]),
1580+
Node::SpaceOrLine,
1581+
Node::text("in"),
1582+
Node::SpaceOrLine,
1583+
Node::Indent(vec![col]),
1584+
Node::SpaceOrLine,
1585+
Node::text("{"),
1586+
];
1587+
let body = self.body(&node.body.values);
1588+
let group = vec![
1589+
self.group(header),
1590+
Node::WrapIf(gid, Box::new(self.group(body))),
1591+
];
1592+
1593+
Node::Group(gid, group)
1594+
}
1595+
15711596
fn if_else(&mut self, node: &nodes::If) -> Node {
15721597
let gid = self.new_group_id();
15731598
let mut group = vec![Node::Nodes(self.conditional(

compiler/src/hir.rs

+91
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use types::{ARRAY_INTERNAL_NAME, ARRAY_PUSH, ARRAY_WITH_CAPACITY};
1414

1515
const BUILTIN_RECEIVER: &str = "_INKO";
1616
const ARRAY_LIT_VAR: &str = "$array";
17+
const ITER_VAR: &str = "$iter";
18+
const NEXT_CALL: &str = "next";
19+
const SOME_CONS: &str = "Some";
1720

1821
struct Comments {
1922
nodes: Vec<ast::Comment>,
@@ -2476,6 +2479,9 @@ impl<'a> LowerToHir<'a> {
24762479
ast::Expression::While(node) => {
24772480
Expression::Loop(self.while_expression(*node))
24782481
}
2482+
ast::Expression::For(node) => {
2483+
Expression::Scope(self.for_expression(*node))
2484+
}
24792485
ast::Expression::Scope(node) => {
24802486
Expression::Scope(self.scope(*node))
24812487
}
@@ -3176,6 +3182,91 @@ impl<'a> LowerToHir<'a> {
31763182
Box::new(Loop { body, location: node.location })
31773183
}
31783184

3185+
fn for_expression(&mut self, node: ast::For) -> Box<Scope> {
3186+
let pat_loc = *node.pattern.location();
3187+
let iter_loc = *node.iterator.location();
3188+
let def_var = Expression::DefineVariable(Box::new(DefineVariable {
3189+
resolved_type: types::TypeRef::Unknown,
3190+
variable_id: None,
3191+
mutable: false,
3192+
name: Identifier {
3193+
name: ITER_VAR.to_string(),
3194+
location: node.location,
3195+
},
3196+
value_type: None,
3197+
value: self.expression(node.iterator),
3198+
location: pat_loc,
3199+
}));
3200+
3201+
let loop_expr = Expression::Loop(Box::new(Loop {
3202+
body: vec![Expression::Match(Box::new(Match {
3203+
resolved_type: types::TypeRef::Unknown,
3204+
// iter.next
3205+
expression: Expression::Call(Box::new(Call {
3206+
kind: types::CallKind::Unknown,
3207+
receiver: Some(Expression::IdentifierRef(Box::new(
3208+
IdentifierRef {
3209+
name: ITER_VAR.to_string(),
3210+
kind: types::IdentifierKind::Unknown,
3211+
usage: Usage::Used,
3212+
location: node.location,
3213+
},
3214+
))),
3215+
name: Identifier {
3216+
name: NEXT_CALL.to_string(),
3217+
location: node.location,
3218+
},
3219+
arguments: Vec::new(),
3220+
parens: false,
3221+
in_mut: false,
3222+
usage: Usage::Used,
3223+
location: iter_loc,
3224+
})),
3225+
cases: vec![
3226+
// case Some(...) -> body
3227+
MatchCase {
3228+
variable_ids: Vec::new(),
3229+
pattern: Pattern::Constructor(Box::new(
3230+
ConstructorPattern {
3231+
constructor_id: None,
3232+
name: Constant {
3233+
name: SOME_CONS.to_string(),
3234+
location: node.location,
3235+
},
3236+
values: vec![self.pattern(node.pattern)],
3237+
location: node.location,
3238+
},
3239+
)),
3240+
guard: None,
3241+
body: self.expressions(node.body),
3242+
location: pat_loc,
3243+
},
3244+
// case _ -> break
3245+
MatchCase {
3246+
variable_ids: Vec::new(),
3247+
pattern: Pattern::Wildcard(Box::new(WildcardPattern {
3248+
location: node.location,
3249+
})),
3250+
guard: None,
3251+
body: vec![Expression::Break(Box::new(Break {
3252+
location: node.location,
3253+
}))],
3254+
location: node.location,
3255+
},
3256+
],
3257+
location: node.location,
3258+
write_result: false,
3259+
}))],
3260+
location: node.location,
3261+
}));
3262+
3263+
Box::new(Scope {
3264+
resolved_type: types::TypeRef::Unknown,
3265+
body: vec![def_var, loop_expr],
3266+
location: node.location,
3267+
})
3268+
}
3269+
31793270
fn scope(&mut self, node: ast::Scope) -> Box<Scope> {
31803271
Box::new(Scope {
31813272
resolved_type: types::TypeRef::Unknown,

std/fixtures/fmt/for_loops/input.inko

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
fn example {
2+
for aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {
3+
foo
4+
}
5+
6+
for (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb {
7+
foo
8+
}
9+
10+
for aaaa in bbbb {
11+
# comment
12+
foo
13+
}
14+
15+
for aaaa in bbbbbbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccc.ddddddddddddddddddddd(10, 20) {
16+
# comment
17+
foo
18+
}
19+
}
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
fn example {
2+
for
3+
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
4+
in
5+
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
6+
{
7+
foo
8+
}
9+
10+
for
11+
(
12+
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
13+
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
14+
)
15+
in
16+
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
17+
{
18+
foo
19+
}
20+
21+
for aaaa in bbbb {
22+
# comment
23+
foo
24+
}
25+
26+
for
27+
aaaa
28+
in
29+
bbbbbbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccc.ddddddddddddddddddddd(
30+
10,
31+
20,
32+
)
33+
{
34+
# comment
35+
foo
36+
}
37+
}

std/src/std/libc/linux.inko

+2-2
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,13 @@ fn extern fsync(fd: Int32) -> Int32
416416

417417
fn extern sendfile(
418418
out: Int32,
419-
in: Int32,
419+
input: Int32,
420420
offset: Pointer[Int64],
421421
count: UInt64,
422422
) -> Int64
423423

424424
fn extern copy_file_range(
425-
in: Int32,
425+
input: Int32,
426426
off_in: Pointer[Int64],
427427
out: Int32,
428428
off_out: Pointer[Int64],

std/src/std/sys/linux/fs.inko

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ fn kernel_copy(from: Int32, to: Int32) -> Result[Int, Error] {
8484
start_blocking
8585

8686
let res = libc.copy_file_range(
87-
in: from,
87+
input: from,
8888
off_in: 0x0 as Pointer[Int64],
8989
out: to,
9090
off_out: 0x0 as Pointer[Int64],

0 commit comments

Comments
 (0)