scout_parser/
lib.rs

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                                // Dont allow non-default params after default params.
173                                // If we dont disallow this then the interpreter will have a
174                                // hard time
175                                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 ident = Identifier::new(self.curr.literal.clone());
202                    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    /// `for <ident> in <expr> do <block> end`
332    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    /// `goto "https://stackoverflow.com"`
352    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    /// `screenshot "screenshot.png"`
360    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    /// `scrape { body: ".body" }`
372    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    /// `{ a: "b", c: "d" }`
379    /// @TODO: allow expressions as values instead of strings
380    ///
381    /// Current token entering should be a LBrace
382    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                // @TODO refactor
448                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                // @TODO refactor
468                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        // self.next_token();
482        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            // let e = self.parse_expr(Precedence::Lowest)?;
585            // args.push(e);
586        }
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}