Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/kwargs #4

Merged
merged 2 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 42 additions & 8 deletions scout-interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<Arc<Object>>,
env: EnvPointer,
Expand All @@ -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);
}
Expand Down Expand Up @@ -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?;
Expand Down Expand Up @@ -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()),
Expand All @@ -740,9 +762,21 @@ fn eval_expression<'a>(
let mut prev: Option<Arc<Object>> = 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?,
};
Expand Down
15 changes: 14 additions & 1 deletion scout-parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,25 @@ pub enum ExprKind {
SelectAll(String, Option<Identifier>),

// Rest
Call(Identifier, Vec<ExprKind>),
Call(CallLiteral),
Chain(Vec<ExprKind>),
Infix(Box<ExprKind>, TokenKind, Box<ExprKind>),
Prefix(Box<ExprKind>, TokenKind),
}

#[derive(Debug, PartialEq, Clone)]
pub struct CallLiteral {
pub ident: Identifier,
pub args: Vec<ExprKind>,
pub kwargs: Vec<Kwarg>,
}

#[derive(Debug, PartialEq, Clone)]
pub struct Kwarg {
pub ident: Identifier,
pub expr: ExprKind,
}

#[derive(Debug, PartialEq, Clone)]
pub struct CrawlLiteral {
pub bindings: Option<CrawlBindings>,
Expand Down
129 changes: 109 additions & 20 deletions scout-parser/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -530,33 +530,74 @@ impl Parser {
Ok(ExprKind::Chain(exprs))
}

fn parse_expr_list(&mut self, end: TokenKind) -> ParseResult<Vec<ExprKind>> {
fn parse_call_args(&mut self, end: TokenKind) -> ParseResult<(Vec<ExprKind>, Vec<Kwarg>)> {
let mut args: Vec<ExprKind> = Vec::new();
let mut kwargs: Vec<Kwarg> = Vec::new();
if self.peek.kind == end {
self.next_token();
return Ok(args);
return Ok((args, kwargs));
}

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)?;
Ok(args)
Ok((args, kwargs))
}

fn parse_call_expr(&mut self, func: ExprKind) -> ParseResult<ExprKind> {
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),
}
Expand All @@ -565,6 +606,8 @@ impl Parser {

#[cfg(test)]
mod tests {
use self::ast::CallLiteral;

use super::*;
use test_case::test_case;

Expand Down Expand Up @@ -624,8 +667,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![]
}
)
)
]
Expand All @@ -642,8 +688,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![]
}
)
])
)
Expand Down Expand Up @@ -707,14 +756,54 @@ 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"
)]
#[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(
Expand Down
Loading