From 476ff58ffbee36ba1145de29c64c804983981bae Mon Sep 17 00:00:00 2001 From: Empa Date: Fri, 5 Jan 2024 12:46:33 +0100 Subject: [PATCH] Conditions and use blocks for functions --- crates/saft-ast/src/lib.rs | 7 ++-- crates/saft-eval/src/interpreter.rs | 45 +++++++++++++++++++----- crates/saft-eval/src/value.rs | 16 +++++++-- crates/saft-lexer/src/lex.rs | 2 ++ crates/saft-lexer/src/token.rs | 4 +++ crates/saft-parser/src/lib.rs | 54 ++++++++++++++++++++++------- 6 files changed, 103 insertions(+), 25 deletions(-) diff --git a/crates/saft-ast/src/lib.rs b/crates/saft-ast/src/lib.rs index 6d03967..8c92e64 100644 --- a/crates/saft-ast/src/lib.rs +++ b/crates/saft-ast/src/lib.rs @@ -36,7 +36,9 @@ pub enum Expr { Vec(Vec>), Grouping(Box>), - Block(Block), + Block(Spanned), + + If(Box>, Spanned, Option>>), Neg(Box>), Not(Box>), @@ -94,6 +96,7 @@ impl Expr { IDiv(..) => "integer division", Not(..) => "not", Block(..) => "block", + If(..) => "if expression", } } } @@ -116,6 +119,6 @@ pub enum Item { Fn { ident: Spanned, params: Vec>, - body: Vec>, + body: Spanned, }, } diff --git a/crates/saft-eval/src/interpreter.rs b/crates/saft-eval/src/interpreter.rs index b8ae904..475d7c3 100644 --- a/crates/saft-eval/src/interpreter.rs +++ b/crates/saft-eval/src/interpreter.rs @@ -1,5 +1,5 @@ use crate::natives::add_natives; -use crate::value::{Cast, Function, NativeFuncData, Num, SaftFunction, Value}; +use crate::value::{Cast, Function, NativeFuncData, Num, SaftFunction, Value, ValueType}; use codespan_reporting::diagnostic::{Diagnostic, Label}; use saft_ast::{Block, Expr, Ident, Item, Module, Statement}; use saft_common::span::{Span, Spanned}; @@ -143,7 +143,7 @@ impl Interpreter { }) => { let fun = Value::Function(Function::SaftFunction(SaftFunction { params: params.clone(), - body: Rc::new(body.clone()), + body: body.clone(), })); self.env.declare(ident, fun); Ok(()) @@ -331,11 +331,7 @@ impl Interpreter { interpreter.env.declare(arg_name, arg.v.clone()); } - for statement in body.iter() { - interpreter.exec_statement(statement)?; - } - - Ok(Value::Nil) + interpreter.eval_block(&body.v) }) { Ok(v) => v, Err(ControlFlow::Return(v)) => v, @@ -586,7 +582,10 @@ impl Interpreter { .ok_or::(cast_error!(v, "numeric"))?), )) } - Expr::Block(Block { stmts, tail }) => self.scoped(|interpreter| { + Expr::Block(Spanned { + v: Block { stmts, tail }, + .. + }) => self.scoped(|interpreter| { for stmt in stmts { interpreter.exec_statement(stmt)?; } @@ -596,8 +595,38 @@ impl Interpreter { None => Ok(Value::Nil), } })?, + Expr::If(box condition, body, else_) => { + let condition = self.eval_expr(condition)?; + let enter = match Cast::::cast(condition.clone()) { + Some(b) => b, + None => return Err(cast_error!(condition, ValueType::Bool.name())), + }; + + if enter { + self.eval_block(&body.v)? + } else if let Some(box expr) = else_ { + self.eval_expr(expr)?.v + } else { + Value::Nil + } + } })) } + + fn eval_block(&mut self, block: impl Borrow) -> Result { + let Block { stmts, tail } = block.borrow(); + + self.scoped(|interpreter| { + for stmt in stmts { + interpreter.exec_statement(stmt)?; + } + + match tail { + Some(box expr) => Ok::<_, ControlFlow>(interpreter.eval_expr(expr)?.v), + None => Ok(Value::Nil), + } + }) + } } impl Default for Interpreter { diff --git a/crates/saft-eval/src/value.rs b/crates/saft-eval/src/value.rs index c1de9ae..ef2e61d 100644 --- a/crates/saft-eval/src/value.rs +++ b/crates/saft-eval/src/value.rs @@ -1,7 +1,7 @@ use crate::interpreter::{ControlFlow, Interpreter}; -use std::{borrow::Borrow, rc::Rc}; +use std::borrow::Borrow; -use saft_ast::Statement; +use saft_ast::Block; use saft_common::span::{Span, Spanned}; #[derive(Debug, Clone)] @@ -239,7 +239,7 @@ pub enum Function { #[derive(Debug, Clone)] pub struct SaftFunction { pub params: Vec>, - pub body: Rc>>, + pub body: Spanned, } #[derive(Clone, Debug)] @@ -391,6 +391,16 @@ impl CastFrom for f64 { } } +impl> CastFrom> for U { + fn ty_name() -> String { + U::ty_name() + } + + fn cast_from(value: Spanned) -> Option { + value.v.cast() + } +} + impl CastFrom for bool { fn ty_name() -> String { "bool".into() diff --git a/crates/saft-lexer/src/lex.rs b/crates/saft-lexer/src/lex.rs index 4903dd0..440d425 100644 --- a/crates/saft-lexer/src/lex.rs +++ b/crates/saft-lexer/src/lex.rs @@ -105,6 +105,8 @@ impl<'a> Lexer<'a> { "or" => mktoken!(cur, T::Or), "true" => mktoken!(cur, T::True), "false" => mktoken!(cur, T::False), + "if" => mktoken!(cur, T::If), + "else" => mktoken!(cur, T::Else), s => mktoken!(cur, T::Identifier(s.into())), } } diff --git a/crates/saft-lexer/src/token.rs b/crates/saft-lexer/src/token.rs index da1ed6c..65c7a0a 100644 --- a/crates/saft-lexer/src/token.rs +++ b/crates/saft-lexer/src/token.rs @@ -41,6 +41,8 @@ pub enum Token { Or, True, False, + If, + Else, } impl Token { @@ -85,6 +87,8 @@ impl Token { Bang => "'!'".into(), True => "'true'".into(), False => "'false'".into(), + If => "'if'".into(), + Else => "'else'".into(), } } } diff --git a/crates/saft-parser/src/lib.rs b/crates/saft-parser/src/lib.rs index cbea9c1..ad0cfa3 100644 --- a/crates/saft-parser/src/lib.rs +++ b/crates/saft-parser/src/lib.rs @@ -190,7 +190,7 @@ impl<'a> Parser<'a> { Ok(stmts) } - pub fn parse_block(&mut self, start: Option) -> Result, Error> { + fn parse_block(&mut self, start: Option) -> Result, Error> { let start = match start { Some(s) => s, None => self.eat(Token::LBrace)?, @@ -206,10 +206,10 @@ impl<'a> Parser<'a> { if let Some(end) = self.try_eat(Token::RBrace) { let s = start.join(end); - return Ok(s.spanned(Expr::Block(Block { + return Ok(s.spanned(Block { stmts, tail: Some(Box::new(e.clone())), - }))); + })); } } Statement::Item(_) => {} @@ -225,7 +225,37 @@ impl<'a> Parser<'a> { let s = start.join(end); - Ok(s.spanned(Expr::Block(Block { stmts, tail: None }))) + Ok(s.spanned(Block { stmts, tail: None })) + } + + fn parse_if(&mut self, start: Option) -> Result, Error> { + let start = match start { + Some(s) => s, + None => self.eat(Token::If)?, + }; + + let condition = self.parse_expr()?; + + let block = self.parse_block(None)?; + + let else_ = if self.try_eat(Token::Else).is_some() { + let peek = self.peek(); + match peek.v { + Token::If => Some(self.parse_if(None)?), + Token::LBrace => { + let block = self.parse_block(None)?; + Some(block.s.clone().spanned(Expr::Block(block))) + } + _ => unexpected!(peek, "'if' or a block"), + } + } else { + None + } + .map(Box::new); + + Ok(start + .join(block.s.clone()) + .spanned(Expr::If(Box::new(condition), block, else_))) } pub fn parse_statement(&mut self) -> Result, Error> { @@ -259,7 +289,8 @@ impl<'a> Parser<'a> { | Token::True | Token::False | Token::Bang - | Token::LBrace => { + | Token::LBrace + | Token::If => { let expr = self.parse_expr()?; Ok(expr.s.clone().spanned(Statement::Expr(expr))) } @@ -324,8 +355,10 @@ impl<'a> Parser<'a> { Ok(s.spanned(Expr::Vec(exprs))) } T::LBrace => { - self.parse_block(Some(start)) + let block = self.parse_block(Some(start))?; + Ok(block.s.clone().spanned(Expr::Block(block))) } + T::If => self.parse_if(Some(start)), T::True => Ok(st.s.spanned(Expr::Bool(true))), T::False => Ok(st.s.spanned(Expr::Bool(false))), @@ -450,11 +483,8 @@ impl<'a> Parser<'a> { self.eat(Token::RParen)?; - self.eat(Token::LBrace)?; - - let body = self.parse_statements(Token::RBrace)?; - - let end = self.eat(Token::RBrace)?; + let body = self.parse_block(None)?; + let s = start.join(&body.s); Ok(Spanned::new( Statement::Item(Item::Fn { @@ -462,7 +492,7 @@ impl<'a> Parser<'a> { params, body, }), - start.join(end), + s, )) } }