1use std::collections::HashMap;
2
3use ast::{
4 CallLiteral, CrawlBindings, CrawlLiteral, ExprKind, FnParam, ForLoop, FuncDef, HashLiteral,
5 Identifier, IfElseLiteral, IfLiteral, Kwarg, Program, StmtKind,
6};
7use scout_lexer::{Lexer, Token, TokenKind};
8
9use crate::ast::{Block, ElseLiteral};
10
11pub mod ast;
12
13type ParseResult<T> = Result<T, ParseError>;
14type PrefixParseFn = fn(parser: &mut Parser) -> ParseResult<ExprKind>;
15type InfixParseFn = fn(parser: &mut Parser, ExprKind) -> ParseResult<ExprKind>;
16
17#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
18enum Precedence {
19 Lowest,
20 Equals,
21 LessGreater,
22 Sum,
23 Product,
24 Call,
25 Index,
26}
27
28impl From<TokenKind> for Precedence {
29 fn from(value: TokenKind) -> Self {
30 use TokenKind::*;
31 match value {
32 And => Self::Equals,
33 Or => Self::Equals,
34 EQ => Self::Equals,
35 NEQ => Self::Equals,
36 LT => Self::LessGreater,
37 GT => Self::LessGreater,
38 LTE => Self::LessGreater,
39 GTE => Self::LessGreater,
40 Plus => Self::Sum,
41 Minus => Self::Sum,
42 Slash => Self::Product,
43 Asterisk => Self::Product,
44 DbColon => Self::Product,
45 LParen => Self::Call,
46 LBracket => Self::Index,
47 Pipe => Self::Index,
48 _ => Self::Lowest,
49 }
50 }
51}
52
53fn map_prefix_fn(kind: &TokenKind) -> Option<PrefixParseFn> {
54 use TokenKind::*;
55 match kind {
56 Ident => Some(Parser::parse_ident),
57 Int => Some(Parser::parse_number_literal),
58 Float => Some(Parser::parse_number_literal),
59 True => Some(Parser::parse_boolean),
60 False => Some(Parser::parse_boolean),
61 Str => Some(Parser::parse_str_literal),
62 Null => Some(Parser::parse_null),
63 LBracket => Some(Parser::parse_list_literal),
64 LBrace => Some(Parser::parse_map),
65 SelectAll => Some(Parser::parse_select_all),
66 Select => Some(Parser::parse_select),
67 Bang => Some(Parser::parse_prefix),
68 _ => None,
69 }
70}
71
72fn map_infix_fn(kind: &TokenKind) -> Option<InfixParseFn> {
73 use TokenKind::*;
74 match kind {
75 Plus => Some(Parser::parse_infix),
76 Minus => Some(Parser::parse_infix),
77 Slash => Some(Parser::parse_infix),
78 Asterisk => Some(Parser::parse_infix),
79 EQ => Some(Parser::parse_infix),
80 NEQ => Some(Parser::parse_infix),
81 LT => Some(Parser::parse_infix),
82 GT => Some(Parser::parse_infix),
83 LTE => Some(Parser::parse_infix),
84 GTE => Some(Parser::parse_infix),
85 And => Some(Parser::parse_infix),
86 Or => Some(Parser::parse_infix),
87 DbColon => Some(Parser::parse_infix),
88 LBracket => Some(Parser::parse_index),
89 LParen => Some(Parser::parse_call_expr),
90 Pipe => Some(Parser::parse_chain_expr),
91 _ => None,
92 }
93}
94
95#[derive(Debug)]
96pub enum ParseError {
97 UnexpectedToken(TokenKind, TokenKind),
98 InvalidToken(TokenKind),
99 InvalidNumber,
100 InvalidFnCall,
101 DefaultFnParamBefore,
102 UnknownPrefix(TokenKind),
103}
104
105pub struct Parser {
106 lex: Lexer,
107 curr: Token,
108 peek: Token,
109}
110
111impl Parser {
112 pub fn new(mut lex: Lexer) -> Self {
113 let curr = lex.next_token();
114 let peek = lex.next_token();
115 Self { lex, curr, peek }
116 }
117
118 pub fn parse_program(&mut self) -> ParseResult<Program> {
119 let mut prgm = Program::default();
120 while self.curr.kind != TokenKind::EOF {
121 let stmt = self.parse_stmt()?;
122 prgm.stmts.push(stmt);
123 self.next_token();
124 }
125 Ok(prgm)
126 }
127
128 fn next_token(&mut self) {
129 let prev = std::mem::replace(&mut self.peek, self.lex.next_token());
130 self.curr = prev;
131 }
132
133 fn expect_peek(&mut self, expected: TokenKind) -> ParseResult<()> {
134 if self.peek.kind == expected {
135 self.next_token();
136 Ok(())
137 } else {
138 Err(ParseError::UnexpectedToken(expected, self.peek.kind))
139 }
140 }
141
142 fn peek_precedence(&self) -> Precedence {
143 Precedence::from(self.peek.kind)
144 }
145
146 fn curr_precedence(&self) -> Precedence {
147 Precedence::from(self.curr.kind)
148 }
149
150 fn parse_stmt(&mut self) -> ParseResult<StmtKind> {
151 let lhs = match self.curr.kind {
152 TokenKind::Def => {
153 self.expect_peek(TokenKind::Ident)?;
154 let ident = Identifier::new(self.curr.literal.clone());
155 self.expect_peek(TokenKind::LParen)?;
156
157 let mut args = Vec::new();
158 let mut has_defaults = false;
159 while self.peek.kind == TokenKind::Comma || self.peek.kind != TokenKind::RParen {
160 self.next_token();
161 match self.curr.kind {
162 TokenKind::Comma => {}
163 TokenKind::Ident => {
164 let ident = Identifier::new(self.curr.literal.clone());
165 let mut default = None;
166 if self.peek.kind == TokenKind::Assign {
167 self.next_token();
168 self.next_token();
169 default = Some(self.parse_expr(Precedence::Lowest)?);
170 has_defaults = true;
171 } else if has_defaults {
172 return Err(ParseError::DefaultFnParamBefore);
176 }
177 args.push(FnParam::new(ident, default));
178 }
179 _ => {
180 return Err(ParseError::InvalidToken(self.curr.kind));
181 }
182 }
183 }
184
185 self.expect_peek(TokenKind::RParen)?;
186 self.expect_peek(TokenKind::Do)?;
187 self.next_token();
188
189 let block = self.parse_block(vec![TokenKind::End])?;
190
191 Ok(StmtKind::Func(FuncDef::new(ident, args, block)))
192 }
193 TokenKind::Goto => self.parse_goto_stmt(),
194 TokenKind::Scrape => self.parse_scrape_stmt(),
195 TokenKind::For => self.parse_for_loop(),
196 TokenKind::While => self.parse_while_loop(),
197 TokenKind::Screenshot => self.parse_screenshot_stmt(),
198 TokenKind::If => self.parse_if_else(),
199 TokenKind::Ident => match self.peek.kind {
200 TokenKind::Assign => {
201 let lhs = self.parse_expr(Precedence::Lowest)?;
203 self.next_token();
204 self.next_token();
205 let val = self.parse_expr(Precedence::Lowest)?;
206 Ok(StmtKind::Assign(lhs, val))
207 }
208 _ => self.parse_expr_stmt(),
209 },
210 TokenKind::Return => {
211 self.next_token();
212 match self.parse_expr(Precedence::Lowest) {
213 Ok(expr) => Ok(StmtKind::Return(Some(expr))),
214 Err(ParseError::InvalidToken(_)) => Ok(StmtKind::Return(None)),
215 Err(e) => Err(e),
216 }
217 }
218 TokenKind::Use => self.parse_use_stmt(),
219 TokenKind::Try => self.parse_try_catch(),
220 TokenKind::Crawl => self.parse_crawl(),
221 _ => self.parse_expr_stmt(),
222 }?;
223
224 match (&lhs, self.peek.kind) {
225 (StmtKind::Expr(expr), TokenKind::Assign) => {
226 self.next_token();
227 self.next_token();
228 let rhs = self.parse_expr(Precedence::Lowest)?;
229 Ok(StmtKind::Assign(expr.clone(), rhs))
230 }
231 _ => Ok(lhs),
232 }
233 }
234
235 fn parse_crawl(&mut self) -> ParseResult<StmtKind> {
236 let mut binding = None;
237 if self.peek.kind == TokenKind::Ident {
238 self.next_token();
239 let link_binding = Identifier::new(self.curr.literal.clone());
240 self.expect_peek(TokenKind::Comma)?;
241 self.expect_peek(TokenKind::Ident)?;
242 let depth_binding = Identifier::new(self.curr.literal.clone());
243 binding = Some(CrawlBindings {
244 link: link_binding,
245 depth: depth_binding,
246 });
247 }
248
249 let mut filter = None;
250 if self.peek.kind == TokenKind::Where {
251 self.next_token();
252 self.next_token();
253 let expr = self.parse_expr(Precedence::Lowest)?;
254 filter = Some(expr);
255 }
256 self.expect_peek(TokenKind::Do)?;
257 self.next_token();
258 let block = self.parse_block(vec![TokenKind::End])?;
259 Ok(StmtKind::Crawl(CrawlLiteral::new(binding, filter, block)))
260 }
261
262 fn parse_if_else(&mut self) -> ParseResult<StmtKind> {
263 let if_lit = self.parse_if()?;
264 let mut elifs = Vec::new();
265 while self.curr.kind == TokenKind::Elif {
266 elifs.push(self.parse_if()?)
267 }
268 let mut else_lit = None;
269 if self.curr.kind == TokenKind::Else {
270 self.next_token();
271 let block = self.parse_block(vec![TokenKind::End])?;
272 else_lit = Some(ElseLiteral { block })
273 }
274
275 Ok(StmtKind::IfElse(IfElseLiteral {
276 if_lit,
277 elifs,
278 else_lit,
279 }))
280 }
281
282 fn parse_if(&mut self) -> ParseResult<IfLiteral> {
283 self.next_token();
284 let cond = self.parse_expr(Precedence::Lowest)?;
285 self.expect_peek(TokenKind::Do)?;
286 self.next_token();
287 let block = self.parse_block(vec![TokenKind::End, TokenKind::Elif, TokenKind::Else])?;
288 Ok(IfLiteral { cond, block })
289 }
290
291 fn parse_prefix(&mut self) -> ParseResult<ExprKind> {
292 let op = self.curr.clone();
293 self.next_token();
294 let expr = self.parse_expr(Precedence::Lowest)?;
295 Ok(ExprKind::Prefix(Box::new(expr), op))
296 }
297
298 fn parse_block(&mut self, finalizers: Vec<TokenKind>) -> ParseResult<Block> {
299 let mut stmts = Vec::new();
300 while !finalizers.contains(&self.curr.kind) {
301 let stmt = self.parse_stmt()?;
302 stmts.push(stmt);
303 self.next_token();
304 }
305 Ok(Block::new(stmts))
306 }
307
308 fn parse_try_catch(&mut self) -> ParseResult<StmtKind> {
309 self.next_token();
310 let try_b = self.parse_block(vec![TokenKind::Catch, TokenKind::End])?;
311 let catch_b = if self.curr.kind == TokenKind::Catch {
312 self.next_token();
313 let block = self.parse_block(vec![TokenKind::End])?;
314 Some(block)
315 } else {
316 None
317 };
318
319 Ok(StmtKind::TryCatch(try_b, catch_b))
320 }
321
322 fn parse_while_loop(&mut self) -> ParseResult<StmtKind> {
323 self.next_token();
324 let condition = self.parse_expr(Precedence::Lowest)?;
325 self.expect_peek(TokenKind::Do)?;
326 self.next_token();
327 let block = self.parse_block(vec![TokenKind::End])?;
328 Ok(StmtKind::WhileLoop(condition, block))
329 }
330
331 fn parse_for_loop(&mut self) -> ParseResult<StmtKind> {
333 self.expect_peek(TokenKind::Ident)?;
334 let ident = Identifier::new(self.curr.literal.clone());
335 self.expect_peek(TokenKind::In)?;
336 self.next_token();
337 let iterable = self.parse_expr(Precedence::Lowest)?;
338 self.expect_peek(TokenKind::Do)?;
339 self.next_token();
340 let block = self.parse_block(vec![TokenKind::End])?;
341 let floop = ForLoop::new(ident, iterable, block);
342 Ok(StmtKind::ForLoop(floop))
343 }
344
345 fn parse_use_stmt(&mut self) -> ParseResult<StmtKind> {
346 self.next_token();
347 let import = self.parse_expr(Precedence::Lowest)?;
348 Ok(StmtKind::Use(import))
349 }
350
351 fn parse_goto_stmt(&mut self) -> ParseResult<StmtKind> {
353 self.next_token();
354 let url = self.parse_expr(Precedence::Lowest)?;
355 let stmt = StmtKind::Goto(url);
356 Ok(stmt)
357 }
358
359 fn parse_screenshot_stmt(&mut self) -> ParseResult<StmtKind> {
361 self.expect_peek(TokenKind::Str)?;
362 let stmt = StmtKind::Screenshot(self.curr.literal.clone());
363 Ok(stmt)
364 }
365
366 fn parse_expr_stmt(&mut self) -> ParseResult<StmtKind> {
367 let expr = self.parse_expr(Precedence::Lowest)?;
368 Ok(StmtKind::Expr(expr))
369 }
370
371 fn parse_scrape_stmt(&mut self) -> ParseResult<StmtKind> {
373 self.expect_peek(TokenKind::LBrace)?;
374 let body = self.parse_hash_literal()?;
375 Ok(StmtKind::Scrape(body))
376 }
377
378 fn parse_hash_literal(&mut self) -> ParseResult<HashLiteral> {
383 let mut pairs = HashMap::new();
384 while self.peek.kind != TokenKind::RBrace {
385 self.expect_peek(TokenKind::Ident)?;
386 let ident = Identifier::new(self.curr.literal.clone());
387 self.expect_peek(TokenKind::Colon)?;
388 self.next_token();
389 let val = self.parse_expr(Precedence::Lowest)?;
390 pairs.insert(ident, val);
391 if self.peek.kind == TokenKind::Comma {
392 self.next_token();
393 }
394 }
395 self.next_token();
396 Ok(HashLiteral { pairs })
397 }
398
399 fn parse_map(&mut self) -> ParseResult<ExprKind> {
400 let lit = self.parse_hash_literal()?;
401 Ok(ExprKind::Map(lit))
402 }
403
404 fn parse_number_literal(&mut self) -> ParseResult<ExprKind> {
405 Ok(ExprKind::Number(
406 self.curr
407 .literal
408 .parse::<f64>()
409 .map_err(|_| ParseError::InvalidNumber)?,
410 ))
411 }
412
413 fn parse_boolean(&mut self) -> ParseResult<ExprKind> {
414 let val = self.curr.kind == TokenKind::True;
415 Ok(ExprKind::Boolean(val))
416 }
417
418 fn parse_str_literal(&mut self) -> ParseResult<ExprKind> {
419 Ok(ExprKind::Str(self.curr.literal.clone()))
420 }
421
422 fn parse_null(&mut self) -> ParseResult<ExprKind> {
423 Ok(ExprKind::Null)
424 }
425
426 fn parse_list_literal(&mut self) -> ParseResult<ExprKind> {
427 self.next_token();
428 let mut content = vec![];
429 while let Ok(expr) = self.parse_expr(Precedence::Lowest) {
430 content.push(expr);
431 self.next_token();
432 if self.curr.kind == TokenKind::Comma {
433 self.next_token();
434 }
435 }
436
437 Ok(ExprKind::List(content))
438 }
439
440 fn parse_select(&mut self) -> ParseResult<ExprKind> {
441 match self.peek.kind {
442 TokenKind::Str => {
443 self.next_token();
444 Ok(ExprKind::Select(self.curr.literal.clone(), None))
445 }
446 TokenKind::LParen => {
447 self.next_token();
449 self.expect_peek(TokenKind::Ident)?;
450 let ident = Identifier::new(self.curr.literal.clone());
451 self.expect_peek(TokenKind::RParen)?;
452 self.expect_peek(TokenKind::Str)?;
453 let expr = ExprKind::Select(self.curr.literal.clone(), Some(ident));
454 Ok(expr)
455 }
456 _ => Err(ParseError::InvalidToken(self.peek.kind)),
457 }
458 }
459
460 fn parse_select_all(&mut self) -> ParseResult<ExprKind> {
461 match self.peek.kind {
462 TokenKind::Str => {
463 self.next_token();
464 Ok(ExprKind::SelectAll(self.curr.literal.clone(), None))
465 }
466 TokenKind::LParen => {
467 self.next_token();
469 self.expect_peek(TokenKind::Ident)?;
470 let ident = Identifier::new(self.curr.literal.clone());
471 self.expect_peek(TokenKind::RParen)?;
472 self.expect_peek(TokenKind::Str)?;
473 let expr = ExprKind::SelectAll(self.curr.literal.clone(), Some(ident));
474 Ok(expr)
475 }
476 _ => Err(ParseError::InvalidToken(self.peek.kind)),
477 }
478 }
479
480 fn parse_infix(&mut self, lhs: ExprKind) -> ParseResult<ExprKind> {
481 let op = self.curr.clone();
483 let prec = self.curr_precedence();
484 self.next_token();
485 let rhs = self.parse_expr(prec)?;
486 Ok(ExprKind::Infix(Box::new(lhs), op, Box::new(rhs)))
487 }
488
489 fn parse_index(&mut self, ident: ExprKind) -> ParseResult<ExprKind> {
490 let infix = self.parse_infix(ident)?;
491 self.expect_peek(TokenKind::RBracket)?;
492 Ok(infix)
493 }
494
495 fn parse_expr(&mut self, precedence: Precedence) -> ParseResult<ExprKind> {
496 match map_prefix_fn(&self.curr.kind) {
497 None => Err(ParseError::UnknownPrefix(self.curr.kind)),
498 Some(f) => {
499 let mut lhs = f(self)?;
500 while precedence < self.peek_precedence() {
501 match map_infix_fn(&self.peek.kind) {
502 None => return Ok(lhs),
503 Some(in_fn) => {
504 self.next_token();
505 lhs = in_fn(self, lhs)?;
506 }
507 }
508 }
509
510 Ok(lhs)
511 }
512 }
513 }
514
515 fn parse_ident(&mut self) -> ParseResult<ExprKind> {
516 Ok(ExprKind::Ident(Identifier::new(self.curr.literal.clone())))
517 }
518
519 fn parse_chain_expr(&mut self, first: ExprKind) -> ParseResult<ExprKind> {
520 let mut exprs = vec![first];
521
522 while self.curr.kind == TokenKind::Pipe {
523 self.next_token();
524 let id = self.parse_ident()?;
525 self.expect_peek(TokenKind::LParen)?;
526 let call = self.parse_call_expr(id)?;
527 exprs.push(call);
528 }
529
530 Ok(ExprKind::Chain(exprs))
531 }
532
533 fn parse_call_args(&mut self, end: TokenKind) -> ParseResult<(Vec<ExprKind>, Vec<Kwarg>)> {
534 let mut args: Vec<ExprKind> = Vec::new();
535 let mut kwargs: Vec<Kwarg> = Vec::new();
536 if self.peek.kind == end {
537 self.next_token();
538 return Ok((args, kwargs));
539 }
540
541 self.next_token();
542 if TokenKind::Ident == self.curr.kind {
543 match self.peek.kind {
544 TokenKind::Assign => {
545 let ident = Identifier::new(self.curr.literal.clone());
546 self.next_token();
547 self.next_token();
548 let expr = self.parse_expr(Precedence::Lowest)?;
549 let kwarg = Kwarg { ident, expr };
550 kwargs.push(kwarg);
551 }
552 _ => {
553 let expr = self.parse_expr(Precedence::Lowest)?;
554 args.push(expr);
555 }
556 }
557 } else {
558 let expr = self.parse_expr(Precedence::Lowest)?;
559 args.push(expr);
560 }
561
562 while self.peek.kind == TokenKind::Comma {
563 self.next_token();
564 self.next_token();
565 if TokenKind::Ident == self.curr.kind {
566 match self.peek.kind {
567 TokenKind::Assign => {
568 let ident = Identifier::new(self.curr.literal.clone());
569 self.next_token();
570 self.next_token();
571 let expr = self.parse_expr(Precedence::Lowest)?;
572 let kwarg = Kwarg { ident, expr };
573 kwargs.push(kwarg);
574 }
575 _ => {
576 let expr = self.parse_expr(Precedence::Lowest)?;
577 args.push(expr);
578 }
579 }
580 } else {
581 let expr = self.parse_expr(Precedence::Lowest)?;
582 args.push(expr);
583 }
584 }
587
588 self.expect_peek(end)?;
589 Ok((args, kwargs))
590 }
591
592 fn parse_call_expr(&mut self, func: ExprKind) -> ParseResult<ExprKind> {
593 match func {
594 ExprKind::Ident(ident) => {
595 let (args, kwargs) = self.parse_call_args(TokenKind::RParen)?;
596 Ok(ExprKind::Call(CallLiteral {
597 ident,
598 args,
599 kwargs,
600 }))
601 }
602 _ => Err(ParseError::InvalidFnCall),
603 }
604 }
605}
606
607#[cfg(test)]
608mod tests {
609 use self::ast::CallLiteral;
610
611 use super::*;
612 use test_case::test_case;
613
614 fn setup_parser(i: &str) -> Parser {
615 Parser::new(Lexer::new(i))
616 }
617
618 fn parse_stmts(i: &str) -> Vec<StmtKind> {
619 let mut p = setup_parser(i);
620 let prg = p.parse_program().unwrap();
621 prg.stmts
622 }
623
624 fn extract_first_stmt(i: &str) -> StmtKind {
625 let stmts = parse_stmts(i);
626 stmts[0].clone()
627 }
628
629 #[test_case(r#"goto "foo""#, StmtKind::Goto(ExprKind::Str("foo".into())); "simple goto")]
630 #[test_case("scrape {}", StmtKind::Scrape(HashLiteral::default()); "empty scrape")]
631 #[test_case(
632 r#"scrape { a: $"b" }"#,
633 StmtKind::Scrape(
634 HashLiteral::from(
635 vec![
636 (Identifier::new("a".into()), ExprKind::Select("b".into(), None))
637 ]
638 )
639 ); "scrape with single key"
640 )]
641 #[test_case(
642 r#"scrape { a: $"b", c: $"d" }"#,
643 StmtKind::Scrape(
644 HashLiteral::from(
645 vec![
646 (Identifier::new("a".into()), ExprKind::Select("b".into(), None)),
647 (Identifier::new("c".into()), ExprKind::Select("d".into(), None))
648 ]
649 )
650 ); "scrape with multi keys"
651 )]
652 #[test_case(
653 r#"scrape { a: $$"b" }"#,
654 StmtKind::Scrape(
655 HashLiteral::from(
656 vec![
657 (Identifier::new("a".into()), ExprKind::SelectAll("b".into(), None)),
658 ]
659 )
660 ); "scrape with select all key"
661 )]
662 #[test_case(
663 r#"scrape { a: fn("a") }"#,
664 StmtKind::Scrape(
665 HashLiteral::from(
666 vec![
667 (
668 Identifier::new("a".into()),
669 ExprKind::Call(
670 CallLiteral {
671 ident: Identifier::new("fn".into()),
672 args: vec![ExprKind::Str("a".into())],
673 kwargs: vec![]
674 }
675 )
676 )
677 ]
678 )
679 ); "scrape with fn key"
680 )]
681 #[test_case(
682 r#"scrape { a: $"b" |> fn("a") }"#,
683 StmtKind::Scrape(
684 HashLiteral::from(
685 vec![
686 (
687 Identifier::new("a".into()),
688 ExprKind::Chain(vec![
689 ExprKind::Select("b".into(), None),
690 ExprKind::Call(
691 CallLiteral {
692 ident: Identifier::new("fn".into()),
693 args: vec![ExprKind::Str("a".into())],
694 kwargs: vec![]
695 }
696 )
697 ])
698 )
699 ]
700 )
701 ); "scrape with pipe key"
702 )]
703 #[test_case(
704 r#"for node in $$"a" do end"#,
705 StmtKind::ForLoop(
706 ForLoop::new(Identifier::new("node".into()), ExprKind::SelectAll("a".into(), None), Block::new(vec![]))
707 ); "for loop empty body"
708 )]
709 #[test_case(
710 r#"for node in $$"a" do $"a" end"#,
711 StmtKind::ForLoop(
712 ForLoop::new(Identifier::new("node".into()), ExprKind::SelectAll("a".into(), None), Block::new(vec![
713 StmtKind::Expr(ExprKind::Select("a".into(), None))
714 ]))
715 ); "for loop single select bodyd"
716 )]
717 #[test_case(
718 r#"x = "a""#,
719 StmtKind::Assign(
720 ExprKind::Ident(Identifier::new("x".into())),
721 ExprKind::Str("a".into())
722 ); "single assign"
723 )]
724 #[test_case(r#"null"#, StmtKind::Expr(ExprKind::Null); "null expr stmt")]
725 #[test_case(
726 r#"for node in $$"a" do scrape {} end"#,
727 StmtKind::ForLoop(
728 ForLoop::new(Identifier::new("node".into()), ExprKind::SelectAll("a".into(), None), Block::new(vec![
729 StmtKind::Scrape(HashLiteral::default())
730 ]))
731 ); "for loop with scrape body"
732 )]
733 #[test_case(
734 r#"x = 1 == 2"#,
735 StmtKind::Assign(
736 ExprKind::Ident(Identifier::new("x".to_string())),
737 ExprKind::Infix(Box::new(ExprKind::Number(1.)), Token::new(TokenKind::EQ, "==".to_string()), Box::new(ExprKind::Number(2.)))
738 ); "assign eq infix"
739 )]
740 #[test_case(
741 "a[0] = 1",
742 StmtKind::Assign(
743 ExprKind::Infix(
744 Box::new(
745 ExprKind::Ident(Identifier::new("a".into()))
746 ),
747 Token::new(TokenKind::LBracket, "[".to_string()),
748 Box::new(
749 ExprKind::Number(0.)
750 )
751 ),
752 ExprKind::Number(1.)
753 ); "index assign"
754 )]
755 #[test_case(
756 r#"f(a, b)"#,
757 StmtKind::Expr(
758 ExprKind::Call(
759 CallLiteral {
760 ident: Identifier::new("f".into()),
761 args: vec![
762 ExprKind::Ident(Identifier::new("a".into())),
763 ExprKind::Ident(Identifier::new("b".into()))
764 ],
765 kwargs: vec![]
766 }
767 )
768 ); "fn call with multi params"
769 )]
770 #[test_case(
771 r#"f(a, b = 1)"#,
772 StmtKind::Expr(
773 ExprKind::Call(
774 CallLiteral {
775 ident: Identifier::new("f".into()),
776 args: vec![
777 ExprKind::Ident(Identifier::new("a".into()))
778 ],
779 kwargs: vec![
780 Kwarg {
781 ident: Identifier::new("b".into()),
782 expr: ExprKind::Number(1.),
783 }
784 ]
785 }
786 )
787 ); "fn call with kwarg & arg"
788 )]
789 #[test_case(
790 r#"f(b = 1)"#,
791 StmtKind::Expr(
792 ExprKind::Call(
793 CallLiteral {
794 ident: Identifier::new("f".into()),
795 args: vec![
796 ],
797 kwargs: vec![
798 Kwarg {
799 ident: Identifier::new("b".into()),
800 expr: ExprKind::Number(1.),
801 }
802 ]
803 }
804 )
805 ); "fn call with kwarg"
806 )]
807 #[test_case(
808 r#"def f() do end"#,
809 StmtKind::Func(
810 FuncDef::new(
811 Identifier::new("f".into()),
812 vec![
813 ],
814 Block::default()
815 )
816 ); "fn definition"
817 )]
818 #[test_case(
819 r#"def f(a, b) do end"#,
820 StmtKind::Func(
821 FuncDef::new(
822 Identifier::new("f".into()),
823 vec![
824 FnParam::new(Identifier::new("a".into()), None),
825 FnParam::new(Identifier::new("b".into()), None)
826 ],
827 Block::default()
828 )
829 ); "fn def multi params"
830 )]
831 #[test_case(
832 r#"def f(a = null) do end"#,
833 StmtKind::Func(
834 FuncDef::new(
835 Identifier::new("f".into()),
836 vec![
837 FnParam::new(Identifier::new("a".into()), Some(ExprKind::Null))
838 ],
839 Block::default()
840 )
841 ); "fn def default param"
842 )]
843 #[test_case(
844 r#"[1, "a"]"#,
845 StmtKind::Expr(
846 ExprKind::List(
847 vec![
848 ExprKind::Number(1.0),
849 ExprKind::Str("a".into()),
850 ]
851 )
852 ); "list literal"
853 )]
854 #[test_case(
855 r#"for a in [1, 2] do end"#,
856 StmtKind::ForLoop(
857 ForLoop::new(Identifier::new("a".into()), ExprKind::List(vec![
858 ExprKind::Number(1.0),
859 ExprKind::Number(2.0),
860 ]), Block::new(vec![]))
861 ); "loop over list literal"
862 )]
863 #[test_case(
864 "try catch end",
865 StmtKind::TryCatch(Block::default(), Some(Block::default())); "empty try catch"
866 )]
867 #[test_case("try end", StmtKind::TryCatch(Block::default(), None); "try catch with no catch")]
868 #[test_case(
869 "a[0]",
870 StmtKind::Expr(
871 ExprKind::Infix(
872 Box::new(ExprKind::Ident(Identifier::new("a".into()))),
873 Token::new(TokenKind::LBracket, "[".to_string()),
874 Box::new(ExprKind::Number(0.))
875 )
876 ); "index"
877 )]
878 #[test_case(
879 "crawl do end",
880 StmtKind::Crawl(CrawlLiteral::new(None, None, Block::default())); "empty crawl stmtddddd"
881 )]
882 #[test_case(
883 "crawl link, depth where depth < 1 do end",
884 StmtKind::Crawl(
885 CrawlLiteral::new(
886 Some(CrawlBindings {
887 link: Identifier::new("link".into()),
888 depth: Identifier::new("depth".into())
889 }),
890 Some(ExprKind::Infix(
891 Box::new(ExprKind::Ident(Identifier::new("depth".into()))),
892 Token::new(TokenKind::LT, "<".to_string()),
893 Box::new(ExprKind::Number(1.))
894 )),
895 Block::default()
896 )
897 ); "crawl stmt with bindings"
898 )]
899 #[test_case(
900 "!true",
901 StmtKind::Expr(ExprKind::Prefix(Box::new(ExprKind::Boolean(true)), Token::new(TokenKind::Bang, "!".to_string()))); "bang prefix"
902 )]
903 #[test_case(
904 "a::b",
905 StmtKind::Expr(
906 ExprKind::Infix(
907 Box::new(
908 ExprKind::Ident(Identifier::new("a".into()))
909 ),
910 Token::new(TokenKind::DbColon, "::".to_string()),
911 Box::new(
912 ExprKind::Ident(Identifier::new("b".into()))
913 )
914 )
915 ); "db colon"
916 )]
917 #[test_case(
918 "while a < 1 do end",
919 StmtKind::WhileLoop(
920 ExprKind::Infix(
921 Box::new(
922 ExprKind::Ident(Identifier::new("a".into()))
923 ),
924 Token::new(TokenKind::LT, "<".to_string()),
925 Box::new(
926 ExprKind::Number(1.)
927 )
928 ),
929 Block::default(),
930 ); "while loop"
931 )]
932 fn test_single_stmt(input: &str, exp: StmtKind) {
933 let stmt = extract_first_stmt(input);
934 assert_eq!(stmt, exp);
935 }
936
937 #[test]
938 fn test_if_else() {
939 let input = r#"if 1 do elif 2 do else end"#;
940 let stmt = extract_first_stmt(input);
941 let exp = StmtKind::IfElse(IfElseLiteral {
942 if_lit: IfLiteral {
943 cond: ExprKind::Number(1.),
944 block: Block::default(),
945 },
946 elifs: vec![IfLiteral {
947 cond: ExprKind::Number(2.),
948 block: Block::default(),
949 }],
950 else_lit: Some(ElseLiteral {
951 block: Block::default(),
952 }),
953 });
954 assert_eq!(stmt, exp);
955 }
956}