Skip to content

Commit 48c4a05

Browse files
committed
Add support for for loops
This fixes #817. Changelog: added
1 parent 8a243f2 commit 48c4a05

File tree

14 files changed

+295
-12
lines changed

14 files changed

+295
-12
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

+18-1
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 {
@@ -790,13 +791,14 @@ impl Expression {
790791
}
791792
}
792793

793-
pub fn is_conditional(&self) -> bool {
794+
pub fn is_conditional_or_loop(&self) -> bool {
794795
matches!(
795796
self,
796797
Expression::While(_)
797798
| Expression::Loop(_)
798799
| Expression::If(_)
799800
| Expression::Match(_)
801+
| Expression::For(_)
800802
)
801803
}
802804

@@ -851,6 +853,7 @@ impl Node for Expression {
851853
Expression::Mut(ref typ) => typ.location(),
852854
Expression::Recover(ref typ) => typ.location(),
853855
Expression::Comment(ref n) => n.location(),
856+
Expression::For(ref n) => n.location(),
854857
}
855858
}
856859
}
@@ -1598,3 +1601,17 @@ impl Node for Module {
15981601
&self.location
15991602
}
16001603
}
1604+
1605+
#[derive(Debug, PartialEq, Eq)]
1606+
pub struct For {
1607+
pub pattern: Pattern,
1608+
pub iterator: Expression,
1609+
pub body: Expressions,
1610+
pub location: Location,
1611+
}
1612+
1613+
impl Node for For {
1614+
fn location(&self) -> &Location {
1615+
&self.location
1616+
}
1617+
}

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

+29-2
Original file line numberDiff line numberDiff line change
@@ -1254,7 +1254,9 @@ impl Document {
12541254
(Expression::Comment(_), _) => Node::HardLine,
12551255
// Conditionals are surrounded by an empty line as to
12561256
// make them stand out more.
1257-
_ if expr.is_conditional() || next.is_conditional() => {
1257+
_ if expr.is_conditional_or_loop()
1258+
|| next.is_conditional_or_loop() =>
1259+
{
12581260
Node::EmptyLine
12591261
}
12601262
// `let` and comments are grouped together.
@@ -1272,7 +1274,7 @@ impl Document {
12721274
};
12731275

12741276
vals.push(sep);
1275-
} else if nodes.len() == 1 && expr.is_conditional() {
1277+
} else if nodes.len() == 1 && expr.is_conditional_or_loop() {
12761278
// Conditionals inside bodies are a bit difficult to read due to
12771279
// all the curly braces, so for expressions such as
12781280
// `if foo { loop { ... } }` we force wrapping across lines.
@@ -1330,6 +1332,7 @@ impl Document {
13301332
Expression::Try(n) => self.try_value(n),
13311333
Expression::Match(n) => self.match_value(n),
13321334
Expression::Scope(n) => self.scope(n),
1335+
Expression::For(n) => self.for_loop(n),
13331336
}
13341337
}
13351338

@@ -1568,6 +1571,30 @@ impl Document {
15681571
Node::Group(gid, group)
15691572
}
15701573

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

compiler/src/hir.rs

+101
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ 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";
20+
const INTO_ITER_CALL: &str = "into_iter";
1721

1822
struct Comments {
1923
nodes: Vec<ast::Comment>,
@@ -2476,6 +2480,9 @@ impl<'a> LowerToHir<'a> {
24762480
ast::Expression::While(node) => {
24772481
Expression::Loop(self.while_expression(*node))
24782482
}
2483+
ast::Expression::For(node) => {
2484+
Expression::Scope(self.for_expression(*node))
2485+
}
24792486
ast::Expression::Scope(node) => {
24802487
Expression::Scope(self.scope(*node))
24812488
}
@@ -3176,6 +3183,100 @@ impl<'a> LowerToHir<'a> {
31763183
Box::new(Loop { body, location: node.location })
31773184
}
31783185

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

std/fixtures/fmt/for_loops/input.inko

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
for aaaa in bbbb {
15+
# comment
16+
foo
17+
}
18+
19+
for aaaa in bbbbbbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccc.ddddddddddddddddddddd(10, 20) {
20+
# comment
21+
foo
22+
}
23+
}

0 commit comments

Comments
 (0)