diff --git a/README.md b/README.md index f245472..8576798 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ Some control flow: `while` and `if/else if/else` Functions: can be defined in both the language and in Rust. -The functions defined in the language currently do not allow returning values. -The `return` keyword does already exist however, it just doesn't do anything yet. +The functions support a return in the form of `return x;` and `x` as the last element. + +As long as the final element is an expression of some kind, it will be seen as a return value in a function. For some sample code, check `test.foo` (extension also TBD) diff --git a/src/function/mod.rs b/src/function/mod.rs index 46e66ad..03f7e48 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -108,7 +108,9 @@ impl Function for InterpreterFunction { sub_env.set_here(name, value.clone()); } for stmt in &self.body { - stmt.eval(&sub_env, map); + if let Some(v) = stmt.eval(&sub_env, map){ + return Ok(v); + } } Ok(Value::Nothing) } diff --git a/src/grammar.pest b/src/grammar.pest index 98409f2..cf3d42a 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -56,10 +56,12 @@ statement = { ( ret | assignment | assignment_let | expression ) ~ ";" | def | w block = { "{" ~ statement* ~ "}" } +func_block = { "{" ~ statement* ~ expression? ~ "}" } + assignment_let = { "let" ~ identifier ~ "=" ~ expression } assignment = { identifier ~ "=" ~ expression } -ret = { "return" ~ expression } +ret = { "return" ~ expression? } while_block = { "while" ~ expression ~ block } @@ -67,7 +69,7 @@ if_block = { "if" ~ expression ~ block ~ ("else if" ~ expression ~ block)* ~ ("e def_params = { "(" ~ (identifier ~ ","?)* ~ ")" } -def = { "def" ~ identifier ~ def_params ~ block } +def = { "def" ~ identifier ~ def_params ~ func_block } equals = { "==" } not_equals = { "!=" } diff --git a/src/pest_parser.rs b/src/pest_parser.rs index 9118fb6..43958fb 100644 --- a/src/pest_parser.rs +++ b/src/pest_parser.rs @@ -116,6 +116,21 @@ fn parse_statement(pair: Pair) -> (Statement, FuncHolder) { let (z, _): (_, FuncHolder) = pair.into_inner().map(parse_statement).unzip(); (Statement::Body(z), FuncHolder::default()) } + Rule::func_block => { + let (statements, expression): (Vec<_>, Vec<_>) = pair + .into_inner() + .partition(|x| x.as_rule() != Rule::expression); + // Only functions defined at the root are supported for now, so we drop the ones + // defined in a block. + let (mut v, _): (Vec, FuncHolder) = + statements.into_iter().map(parse_statement).unzip(); + + if let Some(expr) = expression.into_iter().next() { + let ret = parse_expression(expr); + v.push(Statement::Return(Some(ret))); + } + (Statement::Body(v), FuncHolder::default()) + } Rule::def => { let mut inner = pair.into_inner(); let name = inner.next().unwrap(); @@ -133,6 +148,17 @@ fn parse_statement(pair: Pair) -> (Statement, FuncHolder) { FuncHolder::default().insert(name.as_str().to_string(), args, vec![body.0]), ) } + Rule::ret => { + let mut inner = pair.into_inner(); + if let Some(expr) = inner.next() { + ( + Statement::Return(Some(parse_expression(expr))), + FuncHolder::default(), + ) + } else { + (Statement::Return(None), FuncHolder::default()) + } + } Rule::EOI => (Statement::Body(vec![]), FuncHolder::default()), x => unreachable!("How did we get to {:?}", x), } diff --git a/src/statement.rs b/src/statement.rs index 105ad64..7c7a3a5 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -1,4 +1,4 @@ -use crate::{env::Env, expression::Expression, function::FunctionMap}; +use crate::{env::Env, expression::Expression, function::FunctionMap, value::Value}; #[derive(Debug, Clone, PartialEq)] pub enum Statement { @@ -7,10 +7,11 @@ pub enum Statement { While(Expression, Box), If(Vec<(Expression, Statement)>, Option>), Body(Vec), + Return(Option), } impl Statement { - pub fn eval(&self, env: &Env, f: &FunctionMap) { + pub fn eval(&self, env: &Env, f: &FunctionMap) -> Option { match self { Statement::Assignment(name, expr, explicit_let) => { let value = expr.eval(env, f); @@ -19,36 +20,56 @@ impl Statement { } else { env.set(name, value); } + None } Statement::Expression(expr) => { expr.eval(env, f); + None } Statement::While(expr, body) => { while expr.eval(env, f).is_truthy() { let body_env = Env::new_with_parent(env); - body.eval(&body_env, f); + if let Some(value) = body.eval(&body_env, f) { + return Some(value); + } } + None } Statement::If(if_side, else_side) => { let mut did_if = false; for (expr, body) in if_side { if expr.eval(env, f).is_truthy() { - body.eval(env, f); + if let Some(value) = body.eval(env, f) { + return Some(value); + } did_if = true; break; } } if !did_if { if let Some(else_side) = else_side { - else_side.eval(env, f); + if let Some(value) = else_side.eval(env, f) { + return Some(value); + } } } + None } Statement::Body(body) => { let body_env = Env::new_with_parent(env); for stmt in body.iter() { - stmt.eval(&body_env, f); + if let Some(value) = stmt.eval(&body_env, f) { + return Some(value); + } + } + None + } + Statement::Return(expr) => { + if let Some(expr) = expr { + Some(expr.eval(env, f)) + } else { + Some(Value::Nothing) } } } diff --git a/test.foo b/test.foo index 548ad23..61efb6d 100644 --- a/test.foo +++ b/test.foo @@ -9,8 +9,19 @@ def test_func(x) { while x < 100 { print(x); x = x + 1; + if x > 10 { + return "bar"; + } } + return "foo"; } -test_func(y); +def add_one(x) { + x + 1 +} + +let x = test_func(y); +print(x); + +print(add_one(41));