sylt_parser/
statement.rs

1use super::*;
2
3/// The different kinds of [Statement]s.
4///
5/// There are both shorter statements like `a = b + 1` as well as longer
6/// statements like `if a { ... } else { ...}`. The variants here include
7/// examples of how they look in the code.
8///
9/// Note that this shouldn't be read as a formal language specification.
10#[derive(Debug, Clone)]
11pub enum StatementKind {
12    /// "Imports" another file.
13    ///
14    /// `use <file>`.
15    Use {
16        file: Identifier,
17    },
18
19    /// Defines a new Blob.
20    ///
21    /// `A :: Blob { <field>.. }`.
22    Blob {
23        name: String,
24        fields: HashMap<String, Type>,
25    },
26
27    /// Prints to standard out.
28    ///
29    /// `print <expression>`.
30    Print {
31        value: Expression,
32    },
33
34    /// Assigns to a variable (`a = <expression>`), optionally with an operator
35    /// applied (`a += <expression>`)
36    Assignment {
37        kind: Op,
38        target: Assignable,
39        value: Expression,
40    },
41
42    /// Defines a new variable.
43    ///
44    /// Example: `a := <expression>`.
45    ///
46    /// Valid definition operators are `::`, `:=` and `: <type> =`.
47    Definition {
48        ident: Identifier,
49        kind: VarKind,
50        ty: Type,
51        value: Expression,
52    },
53
54    /// Makes your code go either here or there.
55    ///
56    /// `if <expression> <statement> [else <statement>]`.
57    If {
58        condition: Expression,
59        pass: Box<Statement>,
60        fail: Box<Statement>,
61    },
62
63    /// Do something as long as something else evaluates to true.
64    ///
65    /// `loop <expression> <statement>`.
66    Loop {
67        condition: Expression,
68        body: Box<Statement>,
69    },
70
71    /// Jump out of a loop.
72    ///
73    /// `break`.
74    Break,
75
76    /// Go back to the start of the loop.
77    ///
78    /// `continue`.
79    Continue,
80
81    /// Handles compile time checks of types.
82    ///
83    /// `:A is :B`
84    IsCheck {
85        lhs: Type,
86        rhs: Type,
87    },
88
89    /// Returns a value from a function.
90    ///
91    /// `ret <expression>`.
92    Ret {
93        value: Expression,
94    },
95
96    /// Groups together statements that are executed after another.
97    ///
98    /// `{ <statement>.. }`.
99    Block {
100        statements: Vec<Statement>,
101    },
102
103    /// A free-standing expression. It's just a `<expression>`.
104    StatementExpression {
105        value: Expression,
106    },
107
108    /// Throws an error if it is ever evaluated.
109    ///
110    /// `<!>`.
111    Unreachable,
112
113    EmptyStatement,
114}
115
116/// What makes up a program. Contains any [StatementKind].
117#[derive(Debug, Clone)]
118pub struct Statement {
119    pub span: Span,
120    pub kind: StatementKind,
121}
122
123pub fn block_statement<'t>(ctx: Context<'t>) -> ParseResult<'t, Statement> {
124    let span = ctx.span();
125    let mut ctx = expect!(ctx, T::LeftBrace, "Expected '{{' at start of block");
126
127    let mut errs = Vec::new();
128    let mut statements = Vec::new();
129    // Parse multiple inner statements until } or EOF
130    while !matches!(ctx.token(), T::RightBrace | T::EOF) {
131        match statement(ctx) {
132            Ok((_ctx, stmt)) => {
133                ctx = _ctx; // assign to outer
134                statements.push(stmt);
135            }
136            Err((_ctx, mut err)) => {
137                ctx = _ctx.pop_skip_newlines(false);  // assign to outer
138                while !matches!(ctx.token(), T::Newline | T::EOF) {
139                    ctx = ctx.skip(1);
140                }
141                ctx = ctx.skip_if(T::Newline);
142                errs.append(&mut err);
143            }
144        }
145    }
146
147    let ctx = expect!(ctx, T::RightBrace, "Expected }} after block statement");
148    if errs.is_empty() {
149        #[rustfmt::skip]
150        return Ok(( ctx, Statement { span, kind: StatementKind::Block { statements } }));
151    } else {
152        Err(( ctx, errs ))
153    }
154}
155
156/// Parse a single [Statement].
157pub fn statement<'t>(ctx: Context<'t>) -> ParseResult<'t, Statement> {
158    use StatementKind::*;
159
160    // Newlines have meaning in statements - thus they shouldn't be skipped.
161    let (ctx, skip_newlines) = ctx.push_skip_newlines(false);
162
163    let span = ctx.span();
164    let (ctx, kind) = match &ctx.tokens[ctx.curr..] {
165        [T::Newline, ..] => (ctx.skip(1), EmptyStatement),
166
167        // Block: `{ <statements> }`
168        [T::LeftBrace, ..] => match (block_statement(ctx), expression(ctx)) {
169            (Ok((ctx, stmt)), _) => (ctx, stmt.kind),
170            (_, Ok((ctx, value))) => (ctx, StatementExpression { value }),
171            (Err((ctx, _)), Err(_)) => {
172                raise_syntax_error!(ctx, "Neither a block nor a valid expression");
173            }
174        },
175
176        // `use a`
177        [T::Use, T::Identifier(name), ..] => (
178            ctx.skip(2),
179            Use {
180                file: Identifier {
181                    span: ctx.skip(1).span(),
182                    name: name.clone(),
183                },
184            },
185        ),
186
187        // `: A is : B`
188        [T::Colon, ..] => {
189            let ctx = ctx.skip(1);
190            let (ctx, lhs) = parse_type(ctx)?;
191            let ctx = expect!(ctx, T::Is, "Expected 'is' after first type in 'is-check' statement");
192            let ctx = expect!(ctx, T::Colon, "Expected ':' - only type constant are allowed in 'is-check' statements");
193            let (ctx, rhs) = parse_type(ctx)?;
194            (ctx, IsCheck { lhs, rhs })
195        }
196
197        [T::Break, ..] => (ctx.skip(1), Break),
198        [T::Continue, ..] => (ctx.skip(1), Continue),
199        [T::Unreachable, ..] => (ctx.skip(1), Unreachable),
200
201        [T::Print, ..] => {
202            let (ctx, value) = expression(ctx.skip(1))?;
203            (ctx, Print { value })
204        }
205
206        // `ret <expression>`
207        [T::Ret, ..] => {
208            let ctx = ctx.skip(1);
209            let (ctx, value) = if matches!(ctx.token(), T::Newline) {
210                (
211                    ctx,
212                    Expression {
213                        span: ctx.span(),
214                        kind: ExpressionKind::Nil,
215                    },
216                )
217            } else {
218                expression(ctx)?
219            };
220            (ctx, Ret { value })
221        }
222
223        // `loop <expression> <statement>`, e.g. `loop a < 10 { a += 1 }`
224        [T::Loop, ..] => {
225            let ctx = ctx.skip(1);
226            let (ctx, condition) = if matches!(ctx.token(), T::LeftBrace) {
227                (
228                    ctx,
229                    Expression { span: ctx.span(), kind: ExpressionKind::Bool(true), },
230                )
231            } else {
232                expression(ctx)?
233            };
234            let (ctx, body) = statement(ctx)?;
235            (
236                ctx,
237                Loop {
238                    condition,
239                    body: Box::new(body),
240                },
241            )
242        }
243
244        // `if <expression> <statement> [else <statement>]`. Note that the else is optional.
245        [T::If, ..] => {
246            let (ctx, skip_newlines) = ctx.push_skip_newlines(true);
247            let (ctx, condition) = expression(ctx.skip(1))?;
248            let ctx = ctx.pop_skip_newlines(skip_newlines);
249
250            let (ctx, pass) = statement(ctx)?;
251            // else?
252            let (ctx, fail) = if matches!(ctx.token(), T::Else) {
253                let (ctx, fail) = statement(ctx.skip(1))?;
254                (ctx, fail)
255            } else {
256                // No else so we insert an empty statement instead.
257                (
258                    ctx,
259                    Statement {
260                        span: ctx.span(),
261                        kind: EmptyStatement,
262                    },
263                )
264            };
265
266            (
267                ctx,
268                If {
269                    condition,
270                    pass: Box::new(pass),
271                    fail: Box::new(fail),
272                },
273            )
274        }
275
276        // Blob declaration: `A :: blob { <fields> }
277        [T::Identifier(name), T::ColonColon, T::Blob, ..] => {
278            let name = name.clone();
279            let ctx = expect!(ctx.skip(3), T::LeftBrace, "Expected '{{' to open blob");
280            let (mut ctx, skip_newlines) = ctx.push_skip_newlines(true);
281
282            let mut fields = HashMap::new();
283            // Parse fields: `a: int`
284            loop {
285                match ctx.token().clone() {
286                    T::Newline => {
287                        ctx = ctx.skip(1);
288                    }
289                    // Done with fields.
290                    T::RightBrace => {
291                        break;
292                    }
293
294                    // Another one.
295                    T::Identifier(field) => {
296                        if fields.contains_key(&field) {
297                            raise_syntax_error!(ctx, "Field '{}' is declared twice", field);
298                        }
299                        ctx = expect!(ctx.skip(1), T::Colon, "Expected ':' after field name");
300                        let (_ctx, ty) = parse_type(ctx)?;
301                        ctx = _ctx; // assign to outer
302                        fields.insert(field, ty);
303
304                        if !matches!(ctx.token(), T::Comma | T::RightBrace) {
305                            raise_syntax_error!(ctx, "Expected a field deliminator ','");
306                        }
307                        ctx = ctx.skip_if(T::Comma);
308                    }
309
310                    _ => {
311                        raise_syntax_error!(ctx, "Expected field name or '}}' in blob statement");
312                    }
313                }
314            }
315
316            let ctx = ctx.pop_skip_newlines(skip_newlines);
317            let ctx = expect!(ctx, T::RightBrace, "Expected '}}' to close blob fields");
318            (ctx, Blob { name, fields })
319        }
320
321        // Constant declaration, e.g. `a :: 1`.
322        [T::Identifier(name), T::ColonColon, ..] => {
323            let ident = Identifier {
324                name: name.clone(),
325                span: ctx.span(),
326            };
327            // Skip identifier and `::`.
328            let ctx = ctx.skip(2);
329
330            // The value to assign.
331            let (ctx, value) = expression(ctx)?;
332
333            (
334                ctx,
335                Definition {
336                    ident,
337                    kind: VarKind::Const,
338                    ty: Type {
339                        span: ctx.span(),
340                        kind: TypeKind::Implied,
341                    },
342                    value,
343                },
344            )
345        }
346
347        // Mutable declaration, e.g. `b := 2`.
348        [T::Identifier(name), T::ColonEqual, ..] => {
349            let ident = Identifier {
350                name: name.clone(),
351                span: ctx.span(),
352            };
353            // Skip identifier and `:=`.
354            let ctx = ctx.skip(2);
355
356            // The value to assign.
357            let (ctx, value) = expression(ctx)?;
358
359            (
360                ctx,
361                Definition {
362                    ident,
363                    kind: VarKind::Mutable,
364                    ty: Type {
365                        span: ctx.span(),
366                        kind: TypeKind::Implied,
367                    },
368                    value,
369                },
370            )
371        }
372
373        // Variable declaration with specified type, e.g. `c : int = 3` or `b : int | bool : false`.
374        [T::Identifier(name), T::Colon, ..] => {
375            let ident = Identifier {
376                name: name.clone(),
377                span: ctx.span(),
378            };
379            // Skip identifier and ':'.
380            let ctx = ctx.skip(2);
381
382            let (ctx, kind, ty) = {
383                let forced = matches!(ctx.token(), T::Bang); // !int
384                let ctx = ctx.skip_if(T::Bang);
385                let (ctx, ty) = parse_type(ctx)?;
386                let kind = match (ctx.token(), forced) {
387                    (T::Colon, true) => VarKind::ForceConst,
388                    (T::Equal, true) => VarKind::ForceMutable,
389                    (T::Colon, false) => VarKind::Const,
390                    (T::Equal, false) => VarKind::Mutable,
391                    (t, _) => {
392                        raise_syntax_error!(
393                            ctx,
394                            "Expected ':' or '=' for definition, but got '{:?}'",
395                            t
396                        );
397                    }
398                };
399                // Skip `:` or `=`.
400                (ctx.skip(1), kind, ty)
401            };
402
403            // The value to define the variable to.
404            let (ctx, value) = expression(ctx)?;
405
406            (
407                ctx,
408                Definition {
409                    ident,
410                    kind,
411                    ty,
412                    value,
413                },
414            )
415        }
416
417        // Expression or assignment. We try assignment first.
418        _ => {
419            /// `a = 5`.
420            fn assignment<'t>(ctx: Context<'t>) -> ParseResult<'t, StatementKind> {
421                // The assignable to assign to.
422                let (ctx, target) = assignable(ctx)?;
423                let kind = match ctx.token() {
424                    T::PlusEqual => Op::Add,
425                    T::MinusEqual => Op::Sub,
426                    T::StarEqual => Op::Mul,
427                    T::SlashEqual => Op::Div,
428                    T::Equal => Op::Nop,
429
430                    t => {
431                        raise_syntax_error!(ctx, "No assignment operation matches '{:?}'", t);
432                    }
433                };
434                // The expression to assign the assignable to.
435                let (ctx, value) = expression(ctx.skip(1))?;
436                Ok((
437                    ctx,
438                    Assignment {
439                        kind,
440                        target,
441                        value,
442                    },
443                ))
444            }
445
446            match (assignment(ctx), expression(ctx)) {
447                (Ok((ctx, kind)), _) => (ctx, kind),
448                (_, Ok((ctx, value))) => (ctx, StatementExpression { value }),
449                (Err((_, mut ass_errs)), Err((_, mut expr_errs))) => {
450                    ass_errs.append(&mut expr_errs);
451                    ass_errs.push(
452                        syntax_error!(ctx, "Neither an assignment or a expression")
453                    );
454                    return Err((ctx, ass_errs));
455                }
456            }
457        }
458    };
459
460    let ctx = ctx.skip_if(T::Newline);
461    let ctx = ctx.pop_skip_newlines(skip_newlines);
462    Ok((ctx, Statement { span, kind }))
463}
464
465/// Parse an outer statement.
466///
467/// Currently all statements are valid outer statements.
468pub fn outer_statement<'t>(ctx: Context<'t>) -> ParseResult<Statement> {
469    let (ctx, stmt) = statement(ctx)?;
470    use StatementKind::*;
471    match stmt.kind {
472        #[rustfmt::skip]
473        Blob { .. }
474        | Definition { .. }
475        | Use { .. }
476        | IsCheck { .. }
477        | EmptyStatement
478        => Ok((ctx, stmt)),
479
480        _ => raise_syntax_error!(ctx, "Not a valid outer statement"),
481    }
482}
483
484#[cfg(test)]
485mod test {
486    use super::*;
487    use super::StatementKind::*;
488
489    // NOTE(ed): Expressions are valid statements! :D
490    test!(statement, statement_expression: "1 + 1" => _);
491    test!(statement, statement_print: "print 1" => _);
492    test!(statement, statement_break: "break" => _);
493    test!(statement, statement_continue: "continue" => _);
494    test!(statement, statement_mut_declaration: "a := 1 + 1" => _);
495    test!(statement, statement_const_declaration: "a :: 1 + 1" => _);
496    test!(statement, statement_mut_type_declaration: "a :int= 1 + 1" => _);
497    test!(statement, statement_const_type_declaration: "a :int: 1 + 1" => _);
498    test!(statement, statement_force_mut_type_declaration: "a :!int= 1 + 1" => _);
499    test!(statement, statement_force_const_type_declaration: "a :!int: 1 + 1" => _);
500    test!(statement, statement_if: "if 1 { print a }" => _);
501    test!(statement, statement_if_else: "if 1 { print a } else { print b }" => _);
502    test!(statement, statement_loop: "loop 1 { print a }" => _);
503    test!(statement, statement_loop_no_condition: "loop { print a }" => _);
504    test!(statement, statement_ret: "ret 1 + 1" => _);
505    test!(statement, statement_ret_newline: "ret \n" => _);
506    test!(statement, statement_unreach: "<!>" => _);
507    test!(statement, statement_blob_empty: "A :: blob {}" => _);
508    test!(statement, statement_blob_comma: "A :: blob { a: int, b: int }" => _);
509    test!(statement, statement_blob_comma_newline: "A :: blob { a: int,\n b: int }" => _);
510    test!(statement, statement_assign: "a = 1" => _);
511    test!(statement, statement_assign_index: "a.b = 1 + 2" => _);
512    test!(statement, statement_add_assign: "a += 2" => _);
513    test!(statement, statement_sub_assign: "a -= 2" => _);
514    test!(statement, statement_mul_assign: "a *= 2" => _);
515    test!(statement, statement_div_assign: "a /= 2" => _);
516    test!(statement, statement_assign_call: "a().b() += 2" => _);
517    test!(statement, statement_assign_call_index: "a.c().c.b /= 4" => _);
518    test!(statement, statement_idek: "a'.c'.c.b()().c = 0" => _);
519
520    test!(statement, statement_is_check: ":A is :B" => IsCheck { .. });
521    test!(statement, statement_is_check_nested: ":A.c.d is :B.d.d" => IsCheck { .. });
522
523    test!(statement, statement_if_newline: "if 1 \n\n+\n 1\n\n < 2 { }" => _);
524
525    test!(statement, statement_skip_newline: "(1 \n\n+\n 1\n\n)" => _);
526    test!(statement, statement_skip_newline_list: "[\n\n 1 \n\n,\n 1\n\n,]" => _);
527    test!(statement, statement_skip_newline_set: "{\n\n 1 \n\n,\n 1\n\n,}" => _);
528    test!(statement, statement_skip_newline_dict: "{\n\n 1: \n3\n,\n 1\n\n:1,}" => _);
529
530    test!(outer_statement, outer_statement_blob: "B :: blob {}\n" => _);
531    test!(outer_statement, outer_statement_blob_no_last_comma: "B :: blob { \na: A\n }\n" => _);
532    test!(outer_statement, outer_statement_blob_yes_last_comma: "B :: blob { \na: A,\n }\n" => _);
533    test!(outer_statement, outer_statement_declaration: "B :: fn -> {}\n" => _);
534    test!(outer_statement, outer_statement_use: "use ABC\n" => _);
535    test!(outer_statement, outer_statement_empty: "\n" => _);
536
537    fail!(statement, statement_blob_newline: "A :: blob { a: int\n b: int }" => _);
538}