From 18383b320d8715393094c7dc36f3b33838329e3b Mon Sep 17 00:00:00 2001 From: Max Mindlin Date: Mon, 22 Jul 2024 11:08:11 -0400 Subject: [PATCH 1/2] kwargs --- scout-parser/src/ast.rs | 15 ++++++++++++- scout-parser/src/lib.rs | 48 +++++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/scout-parser/src/ast.rs b/scout-parser/src/ast.rs index 11194a8..8ab8c3e 100644 --- a/scout-parser/src/ast.rs +++ b/scout-parser/src/ast.rs @@ -47,12 +47,25 @@ pub enum ExprKind { SelectAll(String, Option), // Rest - Call(Identifier, Vec), + Call(CallLiteral), Chain(Vec), Infix(Box, TokenKind, Box), Prefix(Box, TokenKind), } +#[derive(Debug, PartialEq, Clone)] +pub struct CallLiteral { + pub ident: Identifier, + pub args: Vec, + pub kwargs: Vec, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Kwarg { + pub ident: Identifier, + pub expr: ExprKind, +} + #[derive(Debug, PartialEq, Clone)] pub struct CrawlLiteral { pub bindings: Option, diff --git a/scout-parser/src/lib.rs b/scout-parser/src/lib.rs index a6e91b2..2c0b8ef 100644 --- a/scout-parser/src/lib.rs +++ b/scout-parser/src/lib.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use ast::{ - CrawlBindings, CrawlLiteral, ExprKind, FnParam, ForLoop, FuncDef, HashLiteral, Identifier, - IfElseLiteral, IfLiteral, Program, StmtKind, + CallLiteral, CrawlBindings, CrawlLiteral, ExprKind, FnParam, ForLoop, FuncDef, HashLiteral, + Identifier, IfElseLiteral, IfLiteral, Kwarg, Program, StmtKind, }; use scout_lexer::{Lexer, Token, TokenKind}; @@ -530,11 +530,12 @@ impl Parser { Ok(ExprKind::Chain(exprs)) } - fn parse_expr_list(&mut self, end: TokenKind) -> ParseResult> { + fn parse_call_args(&mut self, end: TokenKind) -> ParseResult<(Vec, Vec)> { let mut args: Vec = Vec::new(); + let mut kwargs: Vec = Vec::new(); if self.peek.kind == end { self.next_token(); - return Ok(args); + return Ok((args, kwargs)); } self.next_token(); @@ -549,14 +550,18 @@ impl Parser { } self.expect_peek(end)?; - Ok(args) + Ok((args, kwargs)) } fn parse_call_expr(&mut self, func: ExprKind) -> ParseResult { match func { ExprKind::Ident(ident) => { - let args = self.parse_expr_list(TokenKind::RParen)?; - Ok(ExprKind::Call(ident, args)) + let (args, kwargs) = self.parse_call_args(TokenKind::RParen)?; + Ok(ExprKind::Call(CallLiteral { + ident, + args, + kwargs, + })) } _ => Err(ParseError::InvalidFnCall), } @@ -565,6 +570,8 @@ impl Parser { #[cfg(test)] mod tests { + use self::ast::CallLiteral; + use super::*; use test_case::test_case; @@ -624,8 +631,11 @@ mod tests { ( Identifier::new("a".into()), ExprKind::Call( - Identifier::new("fn".into()), - vec![ExprKind::Str("a".into())] + CallLiteral { + ident: Identifier::new("fn".into()), + args: vec![ExprKind::Str("a".into())], + kwargs: vec![] + } ) ) ] @@ -642,8 +652,11 @@ mod tests { ExprKind::Chain(vec![ ExprKind::Select("b".into(), None), ExprKind::Call( - Identifier::new("fn".into()), - vec![ExprKind::Str("a".into())] + CallLiteral { + ident: Identifier::new("fn".into()), + args: vec![ExprKind::Str("a".into())], + kwargs: vec![] + } ) ]) ) @@ -707,11 +720,14 @@ mod tests { r#"f(a, b)"#, StmtKind::Expr( ExprKind::Call( - Identifier::new("f".into()), - vec![ - ExprKind::Ident(Identifier::new("a".into())), - ExprKind::Ident(Identifier::new("b".into())) - ] + CallLiteral { + ident: Identifier::new("f".into()), + args: vec![ + ExprKind::Ident(Identifier::new("a".into())), + ExprKind::Ident(Identifier::new("b".into())) + ], + kwargs: vec![] + } ) ); "fn call with multi params" )] From 3374345426c288d5dc90b473d5df34f26a9e488f Mon Sep 17 00:00:00 2001 From: maxmindlin <35264981+maxmindlin@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:26:07 -0400 Subject: [PATCH 2/2] kwarg eval --- scout-interpreter/src/lib.rs | 50 ++++++++++++++++++---- scout-parser/src/lib.rs | 81 ++++++++++++++++++++++++++++++++++-- 2 files changed, 119 insertions(+), 12 deletions(-) diff --git a/scout-interpreter/src/lib.rs b/scout-interpreter/src/lib.rs index 0d84ae1..634775f 100644 --- a/scout-interpreter/src/lib.rs +++ b/scout-interpreter/src/lib.rs @@ -12,7 +12,8 @@ use futures::{future::BoxFuture, FutureExt}; use object::{obj_map_to_json, Object}; use scout_lexer::{Lexer, TokenKind}; use scout_parser::ast::{ - Block, CrawlLiteral, ExprKind, Identifier, IfElseLiteral, NodeKind, Program, StmtKind, + Block, CallLiteral, CrawlLiteral, ExprKind, Identifier, IfElseLiteral, Kwarg, NodeKind, + Program, StmtKind, }; use scout_parser::{ParseError, Parser}; use serde::{Deserialize, Serialize}; @@ -564,7 +565,8 @@ async fn eval_block( fn apply_call<'a>( ident: &'a Identifier, - params: &'a [ExprKind], + args: &'a [ExprKind], + kwargs: &'a [Kwarg], crawler: &'a fantoccini::Client, prev: Option>, env: EnvPointer, @@ -573,7 +575,7 @@ fn apply_call<'a>( async move { // Evaluate the provided fn inputs let mut obj_params = Vec::new(); - for param in params.iter() { + for param in args.iter() { let expr = eval_expression(param, crawler, env.clone(), results.clone()).await?; obj_params.push(expr); } @@ -618,6 +620,13 @@ fn apply_call<'a>( } } + for kwarg in kwargs.iter() { + let val = + eval_expression(&kwarg.expr, crawler, env.clone(), results.clone()) + .await?; + scope.set(&kwarg.ident, val).await; + } + let ev = eval_block(block, crawler, Arc::new(Mutex::new(scope)), results.clone()) .await?; @@ -729,8 +738,21 @@ fn eval_expression<'a>( Ok(Arc::new(Object::Map(Mutex::new(out)))) } - ExprKind::Call(ident, params) => { - apply_call(ident, params, crawler, None, env.clone(), results.clone()).await + ExprKind::Call(CallLiteral { + ident, + args, + kwargs, + }) => { + apply_call( + ident, + args, + kwargs, + crawler, + None, + env.clone(), + results.clone(), + ) + .await } ExprKind::Ident(ident) => match env.lock().await.get(ident).await { Some(obj) => Ok(obj.clone()), @@ -740,9 +762,21 @@ fn eval_expression<'a>( let mut prev: Option> = None; for expr in exprs { let eval = match expr { - ExprKind::Call(ident, params) => { - apply_call(ident, params, crawler, prev, env.clone(), results.clone()) - .await? + ExprKind::Call(CallLiteral { + ident, + args, + kwargs, + }) => { + apply_call( + ident, + args, + kwargs, + crawler, + prev, + env.clone(), + results.clone(), + ) + .await? } _ => eval_expression(expr, crawler, env.clone(), results.clone()).await?, }; diff --git a/scout-parser/src/lib.rs b/scout-parser/src/lib.rs index 2c0b8ef..9bc9763 100644 --- a/scout-parser/src/lib.rs +++ b/scout-parser/src/lib.rs @@ -539,14 +539,50 @@ impl Parser { } self.next_token(); - let expr = self.parse_expr(Precedence::Lowest)?; - args.push(expr); + if TokenKind::Ident == self.curr.kind { + match self.peek.kind { + TokenKind::Assign => { + let ident = Identifier::new(self.curr.literal.clone()); + self.next_token(); + self.next_token(); + let expr = self.parse_expr(Precedence::Lowest)?; + let kwarg = Kwarg { ident, expr }; + kwargs.push(kwarg); + } + _ => { + let expr = self.parse_expr(Precedence::Lowest)?; + args.push(expr); + } + } + } else { + let expr = self.parse_expr(Precedence::Lowest)?; + args.push(expr); + } while self.peek.kind == TokenKind::Comma { self.next_token(); self.next_token(); - let e = self.parse_expr(Precedence::Lowest)?; - args.push(e); + if TokenKind::Ident == self.curr.kind { + match self.peek.kind { + TokenKind::Assign => { + let ident = Identifier::new(self.curr.literal.clone()); + self.next_token(); + self.next_token(); + let expr = self.parse_expr(Precedence::Lowest)?; + let kwarg = Kwarg { ident, expr }; + kwargs.push(kwarg); + } + _ => { + let expr = self.parse_expr(Precedence::Lowest)?; + args.push(expr); + } + } + } else { + let expr = self.parse_expr(Precedence::Lowest)?; + args.push(expr); + } + // let e = self.parse_expr(Precedence::Lowest)?; + // args.push(e); } self.expect_peek(end)?; @@ -731,6 +767,43 @@ mod tests { ) ); "fn call with multi params" )] + #[test_case( + r#"f(a, b = 1)"#, + StmtKind::Expr( + ExprKind::Call( + CallLiteral { + ident: Identifier::new("f".into()), + args: vec![ + ExprKind::Ident(Identifier::new("a".into())) + ], + kwargs: vec![ + Kwarg { + ident: Identifier::new("b".into()), + expr: ExprKind::Number(1.), + } + ] + } + ) + ); "fn call with kwarg & arg" + )] + #[test_case( + r#"f(b = 1)"#, + StmtKind::Expr( + ExprKind::Call( + CallLiteral { + ident: Identifier::new("f".into()), + args: vec![ + ], + kwargs: vec![ + Kwarg { + ident: Identifier::new("b".into()), + expr: ExprKind::Number(1.), + } + ] + } + ) + ); "fn call with kwarg" + )] #[test_case( r#"def f() do end"#, StmtKind::Func(