1use super::*;
2
3#[derive(Debug, Clone)]
11pub enum StatementKind {
12 Use {
16 file: Identifier,
17 },
18
19 Blob {
23 name: String,
24 fields: HashMap<String, Type>,
25 },
26
27 Print {
31 value: Expression,
32 },
33
34 Assignment {
37 kind: Op,
38 target: Assignable,
39 value: Expression,
40 },
41
42 Definition {
48 ident: Identifier,
49 kind: VarKind,
50 ty: Type,
51 value: Expression,
52 },
53
54 If {
58 condition: Expression,
59 pass: Box<Statement>,
60 fail: Box<Statement>,
61 },
62
63 Loop {
67 condition: Expression,
68 body: Box<Statement>,
69 },
70
71 Break,
75
76 Continue,
80
81 IsCheck {
85 lhs: Type,
86 rhs: Type,
87 },
88
89 Ret {
93 value: Expression,
94 },
95
96 Block {
100 statements: Vec<Statement>,
101 },
102
103 StatementExpression {
105 value: Expression,
106 },
107
108 Unreachable,
112
113 EmptyStatement,
114}
115
116#[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 while !matches!(ctx.token(), T::RightBrace | T::EOF) {
131 match statement(ctx) {
132 Ok((_ctx, stmt)) => {
133 ctx = _ctx; statements.push(stmt);
135 }
136 Err((_ctx, mut err)) => {
137 ctx = _ctx.pop_skip_newlines(false); 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
156pub fn statement<'t>(ctx: Context<'t>) -> ParseResult<'t, Statement> {
158 use StatementKind::*;
159
160 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 [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 [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 [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 [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 [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 [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 let (ctx, fail) = if matches!(ctx.token(), T::Else) {
253 let (ctx, fail) = statement(ctx.skip(1))?;
254 (ctx, fail)
255 } else {
256 (
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 [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 loop {
285 match ctx.token().clone() {
286 T::Newline => {
287 ctx = ctx.skip(1);
288 }
289 T::RightBrace => {
291 break;
292 }
293
294 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; 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 [T::Identifier(name), T::ColonColon, ..] => {
323 let ident = Identifier {
324 name: name.clone(),
325 span: ctx.span(),
326 };
327 let ctx = ctx.skip(2);
329
330 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 [T::Identifier(name), T::ColonEqual, ..] => {
349 let ident = Identifier {
350 name: name.clone(),
351 span: ctx.span(),
352 };
353 let ctx = ctx.skip(2);
355
356 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 [T::Identifier(name), T::Colon, ..] => {
375 let ident = Identifier {
376 name: name.clone(),
377 span: ctx.span(),
378 };
379 let ctx = ctx.skip(2);
381
382 let (ctx, kind, ty) = {
383 let forced = matches!(ctx.token(), T::Bang); 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 (ctx.skip(1), kind, ty)
401 };
402
403 let (ctx, value) = expression(ctx)?;
405
406 (
407 ctx,
408 Definition {
409 ident,
410 kind,
411 ty,
412 value,
413 },
414 )
415 }
416
417 _ => {
419 fn assignment<'t>(ctx: Context<'t>) -> ParseResult<'t, StatementKind> {
421 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 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
465pub 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 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}