diff --git a/examples/fib.ly b/examples/fib.ly index f8a40432..34883d1b 100644 --- a/examples/fib.ly +++ b/examples/fib.ly @@ -2,7 +2,7 @@ fun fib($n) { if ($n < 2) return $n; return fib($n - 2) + fib($n - 1); -} +}; var $start_ly = clock(); print(fib(35) == 9227465); diff --git a/examples/fn.ly b/examples/fn.ly index 119351ff..43a65694 100644 --- a/examples/fn.ly +++ b/examples/fn.ly @@ -13,7 +13,7 @@ fun helloWorld ($message) { print("outer"); } } -} +}; for (var $i = 0; $i < 10; $i = $i + 1) { print(helloWorld("My name is Lykia.")); diff --git a/examples/scan_err.ly b/examples/scan_err.ly index eecddc7f..0c72e47c 100644 --- a/examples/scan_err.ly +++ b/examples/scan_err.ly @@ -2,6 +2,6 @@ fun fib($n) { if ($n < 2) return $n; return fib($n - 2) + fib($n - 1); -} +}; 117E \ No newline at end of file diff --git a/src/lang/ast/expr.rs b/src/lang/ast/expr.rs index c4ab1407..deeaf2b1 100644 --- a/src/lang/ast/expr.rs +++ b/src/lang/ast/expr.rs @@ -1,6 +1,8 @@ +use std::rc::Rc; + use crate::{lang::token::Token, runtime::types::RV}; -use super::sql::SqlSelect; +use super::{sql::SqlSelect, stmt::StmtId}; #[derive(Debug, Eq, PartialEq)] pub enum Expr { @@ -8,6 +10,7 @@ pub enum Expr { Variable(Token), Grouping(ExprId), Literal(RV), + Function(Token, Vec, Rc>), Binary { left: ExprId, token: Token, @@ -32,5 +35,5 @@ pub enum Expr { args: Vec, }, } - -pub type ExprId = usize; +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct ExprId(pub usize); diff --git a/src/lang/ast/mod.rs b/src/lang/ast/mod.rs index 16b90399..6602689d 100644 --- a/src/lang/ast/mod.rs +++ b/src/lang/ast/mod.rs @@ -27,19 +27,19 @@ impl ParserArena { pub fn expression(&mut self, expr: Expr) -> ExprId { self.expressions.push(expr); - self.expressions.len() - 1 + ExprId(self.expressions.len() - 1) } pub fn statement(&mut self, stmt: Stmt) -> StmtId { self.statements.push(stmt); - self.statements.len() - 1 + StmtId(self.statements.len() - 1) } pub fn get_expression(&self, idx: ExprId) -> &Expr { - &self.expressions[idx] + &self.expressions[idx.0] } pub fn get_statement(&self, idx: StmtId) -> &Stmt { - &self.statements[idx] + &self.statements[idx.0] } } diff --git a/src/lang/ast/stmt.rs b/src/lang/ast/stmt.rs index b4116f99..60e3f76a 100644 --- a/src/lang/ast/stmt.rs +++ b/src/lang/ast/stmt.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use crate::lang::token::Token; use super::expr::ExprId; @@ -7,7 +5,6 @@ use super::expr::ExprId; #[derive(Debug, Eq, PartialEq)] pub enum Stmt { Expression(ExprId), - Function(Token, Vec, Rc>), Declaration(Token, ExprId), Block(Vec), If(ExprId, StmtId, Option), @@ -17,4 +14,5 @@ pub enum Stmt { Return(Token, Option), } -pub type StmtId = usize; +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct StmtId(pub usize); diff --git a/src/lang/parser.rs b/src/lang/parser.rs index fd2ec5a6..a42edbf1 100644 --- a/src/lang/parser.rs +++ b/src/lang/parser.rs @@ -108,7 +108,6 @@ impl<'a> Parser<'a> { fn declaration(&mut self) -> ParseResult { match_next!(self, kw!(Var), var_declaration); - match_next!(self, kw!(Fun), fun_declaration); self.statement() } @@ -236,7 +235,17 @@ impl<'a> Parser<'a> { Ok(self.arena.statement(Stmt::Expression(expr))) } - fn fun_declaration(&mut self) -> ParseResult { + fn var_declaration(&mut self) -> ParseResult { + let token = self.expected(Identifier { dollar: true })?.clone(); + let expr = match self.match_next(sym!(Equal)) { + true => self.expression()?, + false => self.arena.expression(Expr::Literal(RV::Null)), + }; + self.expected(sym!(Semicolon))?; + Ok(self.arena.statement(Stmt::Declaration(token, expr))) + } + + fn fun_declaration(&mut self) -> ParseResult { let token = self.expected(Identifier { dollar: false })?.clone(); self.expected(sym!(LeftParen))?; let mut parameters: Vec = vec![]; @@ -254,27 +263,18 @@ impl<'a> Parser<'a> { let block = self.arena.get_statement(bidx); - let body: Vec = match block { + let body: Vec = match block { Stmt::Block(stmts) => stmts.clone(), _ => vec![], }; Ok(self .arena - .statement(Stmt::Function(token, parameters, Rc::new(body)))) - } - - fn var_declaration(&mut self) -> ParseResult { - let token = self.expected(Identifier { dollar: true })?.clone(); - let expr = match self.match_next(sym!(Equal)) { - true => self.expression()?, - false => self.arena.expression(Expr::Literal(RV::Null)), - }; - self.expected(sym!(Semicolon))?; - Ok(self.arena.statement(Stmt::Declaration(token, expr))) + .expression(Expr::Function(token, parameters, Rc::new(body)))) } fn expression(&mut self) -> ParseResult { + match_next!(self, kw!(Fun), fun_declaration); self.assignment() } diff --git a/src/lang/visualization.rs b/src/lang/visualization.rs index a7890e41..eb721b2e 100644 --- a/src/lang/visualization.rs +++ b/src/lang/visualization.rs @@ -97,6 +97,21 @@ impl Parsed { } buf } + Expr::Function(tok, args, body) => { + let mut buf = indent( + level, + &format!("FunctionDeclaration [{} (", tok.span.lexeme.as_ref()), + false, + ); + for arg in args { + buf.push_str(&format!("{},", arg.span.lexeme.as_ref())); + } + buf.push_str(")]"); + for stmt in body.as_ref() { + buf.push_str(&self.visit_stmt(stmt.clone(), level + 1)?); + } + buf + } }; Ok(matched) @@ -161,21 +176,6 @@ impl Parsed { } Ok(buf) } - Stmt::Function(tok, args, body) => { - let mut buf = indent( - level, - &format!("FunctionDeclaration [{} (", tok.span.lexeme.as_ref()), - false, - ); - for arg in args { - buf.push_str(&format!("{},", arg.span.lexeme.as_ref())); - } - buf.push_str(")]"); - for expr in body.as_ref() { - buf.push_str(&self.visit_stmt(*expr, level + 1)?); - } - Ok(buf) - } } } diff --git a/src/runtime/interpreter.rs b/src/runtime/interpreter.rs index 1f0c2b82..85ed8505 100644 --- a/src/runtime/interpreter.rs +++ b/src/runtime/interpreter.rs @@ -287,6 +287,25 @@ impl Visitor for Interpreter { })) } } + Expr::Function(token, parameters, body) => { + let name = token.span.lexeme.as_ref().to_string(); + let fun = Function::UserDefined { + name: name.clone(), + body: Rc::clone(body), + parameters: parameters + .iter() + .map(|x| x.span.lexeme.as_ref().to_string()) + .collect(), + closure: self.env.clone(), + }; + + let callable = Callable(Some(parameters.len()), fun.into()); + + // TODO(vck): Callable shouldn't be cloned here + self.env.borrow_mut().declare(name, callable.clone()); + + return Ok(callable); + } } } @@ -354,22 +373,6 @@ impl Visitor for Interpreter { } return Err(HaltReason::Return(RV::Undefined)); } - Stmt::Function(token, parameters, body) => { - let name = token.span.lexeme.as_ref().to_string(); - let fun = Function::UserDefined { - name: name.clone(), - body: Rc::clone(body), - parameters: parameters - .iter() - .map(|x| x.span.lexeme.as_ref().to_string()) - .collect(), - closure: self.env.clone(), - }; - - self.env - .borrow_mut() - .declare(name, Callable(Some(parameters.len()), fun.into())); - } } Ok(RV::Undefined) } diff --git a/src/runtime/resolver.rs b/src/runtime/resolver.rs index 0140e951..0c558dd2 100644 --- a/src/runtime/resolver.rs +++ b/src/runtime/resolver.rs @@ -27,7 +27,7 @@ impl Resolver { } pub fn get_distance(&self, eid: ExprId) -> Option { - self.locals.get(&eid).copied() + self.locals.get(&eid.0).copied() } pub fn begin_scope(&mut self) { @@ -55,7 +55,7 @@ impl Resolver { pub fn resolve_local(&mut self, expr: ExprId, name: &Token) { for i in (0..self.scopes.len()).rev() { if self.scopes[i].contains_key(&name.span.lexeme.as_ref().to_string()) { - self.locals.insert(expr, self.scopes.len() - 1 - i); + self.locals.insert(expr.0, self.scopes.len() - 1 - i); return; } } @@ -133,6 +133,17 @@ impl Visitor for Resolver { self.resolve_expr(*argument); } } + Expr::Function(_token, parameters, body) => { + self.declare(_token); + self.define(_token); + self.begin_scope(); + for param in parameters { + self.declare(param); + self.define(param); + } + self.resolve_stmts(body.as_ref()); + self.end_scope(); + } Expr::Select(_) => (), }; Ok(RV::Undefined) @@ -175,17 +186,6 @@ impl Visitor for Resolver { self.resolve_expr(expr.unwrap()); } } - Stmt::Function(_token, parameters, body) => { - self.declare(_token); - self.define(_token); - self.begin_scope(); - for param in parameters { - self.declare(param); - self.define(param); - } - self.resolve_stmts(body.as_ref()); - self.end_scope(); - } } Ok(RV::Undefined) } diff --git a/src/runtime/tests/functions.rs b/src/runtime/tests/functions.rs index c628871d..b914d18c 100644 --- a/src/runtime/tests/functions.rs +++ b/src/runtime/tests/functions.rs @@ -9,11 +9,11 @@ mod test { exec_assert( "fun f($x, $q) { $x($q); - } + }; fun g($q) { print($q); - } + }; for (var $i=0; $i<10; $i = $i + 1) { f(g, $i); @@ -41,10 +41,10 @@ mod test { fun count() { $i = $i + 1; print($i); - } + }; return count; - } + }; var $count = makeCounter(); $count(); $count();", @@ -59,7 +59,7 @@ mod test { { fun showA() { print($a); - } + }; showA(); var $a = \"block\"; @@ -79,14 +79,14 @@ mod test { { fun showA() { print($a); - } + }; showA(); var $a = \"block\"; showA(); fun showB() { print($a); - } + }; showB(); }", vec![ @@ -105,7 +105,7 @@ mod test { { fun showA() { print($a); - } + }; showA(); var $a = \"block\"; @@ -126,13 +126,13 @@ mod test { { fun showA() { print($a); - } + }; var $a = \"block\"; fun showB() { print($a); - } + }; // showA(); @@ -151,4 +151,30 @@ mod test { ], ); } + + #[test] + fn test_anonymous_fn_0() { + exec_assert( + "var $pr = fun a() { + print(\"hello\"); + }; + + $pr(); + ", + vec![ + RV::Str(Rc::new("hello".to_string())), + ], + ); + } + + #[test] + fn test_anonymous_fn_1() { + exec_assert( + "(fun a() { + print(\"hello\"); + })(); + ", + vec![RV::Str(Rc::new("hello".to_string()))], + ); + } }