From c69216082eeb619324a01dc644012701254a1ab9 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Mon, 18 Apr 2022 19:57:28 -0400 Subject: [PATCH] =?UTF-8?q?add=20lambdas=20(=CE=BB)=20and=20brainfuck=20la?= =?UTF-8?q?mbdas=20(bf=CE=BB)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ablescript/src/ast.rs | 20 +++++-- ablescript/src/interpret.rs | 98 +++++++++++++++++++++---------- ablescript/src/lexer.rs | 8 +++ ablescript/src/parser.rs | 111 +++++++++++++++++++++++++++++------- examples/lambda.able | 7 +++ 5 files changed, 190 insertions(+), 54 deletions(-) create mode 100644 examples/lambda.able diff --git a/ablescript/src/ast.rs b/ablescript/src/ast.rs index 50f9ceb..39fe1eb 100644 --- a/ablescript/src/ast.rs +++ b/ablescript/src/ast.rs @@ -124,13 +124,11 @@ pub enum Stmt { Functio { ident: Spanned, - params: Vec>, - body: Block, + body: Spanned, }, BfFunctio { ident: Spanned, - tape_len: Option>, - code: Vec, + body: Spanned, }, Call { expr: Spanned, @@ -143,6 +141,18 @@ pub enum Stmt { Rickroll, } +#[derive(Debug, PartialEq, Clone, Hash)] +pub struct FunctioBody { + pub params: Vec>, + pub body: Block, +} + +#[derive(Debug, PartialEq, Clone, Hash)] +pub struct BfFunctioBody { + pub tape_len: Option>, + pub code: Vec, +} + /// Expression is parse unit which do not cause any effect, /// like math and logical operations or values. #[derive(Debug, PartialEq, Clone, Hash)] @@ -155,6 +165,8 @@ pub enum Expr { Aint(Box>), Literal(Literal), Cart(Vec<(Spanned, Spanned)>), + Lambda(Spanned), + BfLambda(Box>), Index { expr: Box>, index: Box>, diff --git a/ablescript/src/interpret.rs b/ablescript/src/interpret.rs index b15967d..a1092ac 100644 --- a/ablescript/src/interpret.rs +++ b/ablescript/src/interpret.rs @@ -9,7 +9,7 @@ #![deny(missing_docs)] use crate::{ - ast::{Assignable, AssignableKind, Expr, Spanned, Stmt}, + ast::{Assignable, AssignableKind, BfFunctioBody, Expr, FunctioBody, Spanned, Stmt}, consts::ablescript_consts, error::{Error, ErrorKind}, variables::{Functio, Value, ValueRef, Variable}, @@ -152,6 +152,32 @@ impl ExecEnv { final_result } + /// Evaluate a functio body, returning its functio. + fn eval_functio_body(&self, body: &Spanned) -> Functio { + Functio::Able { + params: body + .item + .params + .iter() + .map(|ident| ident.item.to_owned()) + .collect(), + body: body.item.body.to_owned(), + } + } + + /// Evaluate a BF functio body, returning its functio or an error. + fn eval_bff_body(&self, body: &Spanned) -> Result { + Ok(Functio::Bf { + instructions: body.item.code.to_owned(), + tape_len: body + .item + .tape_len + .as_ref() + .map(|tape_len| self.eval_expr(tape_len).map(|v| v.into_isize() as usize)) + .unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?, + }) + } + /// Evaluate an Expr, returning its value or an error. fn eval_expr(&self, expr: &Spanned) -> Result { use crate::ast::BinOpKind::*; @@ -174,6 +200,8 @@ impl ExecEnv { } Aint(expr) => !self.eval_expr(expr)?, Literal(lit) => lit.clone().into(), + Lambda(body) => Value::Functio(self.eval_functio_body(body)), + BfLambda(body) => Value::Functio(self.eval_bff_body(body)?), Expr::Cart(members) => Value::Cart( members .iter() @@ -216,36 +244,11 @@ impl ExecEnv { self.decl_var(&ident.item, init); } - Stmt::Functio { - ident, - params, - body, - } => { - self.decl_var( - &ident.item, - Value::Functio(Functio::Able { - params: params.iter().map(|ident| ident.item.to_owned()).collect(), - body: body.to_owned(), - }), - ); + Stmt::Functio { ident, body } => { + self.decl_var(&ident.item, Value::Functio(self.eval_functio_body(body))); } - Stmt::BfFunctio { - ident, - tape_len, - code, - } => { - self.decl_var( - &ident.item, - Value::Functio(Functio::Bf { - instructions: code.to_owned(), - tape_len: tape_len - .as_ref() - .map(|tape_len| { - self.eval_expr(tape_len).map(|v| v.into_isize() as usize) - }) - .unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?, - }), - ); + Stmt::BfFunctio { ident, body } => { + self.decl_var(&ident.item, Value::Functio(self.eval_bff_body(body)?)); } Stmt::Unless { cond, body } => { if !self.eval_expr(cond)?.into_abool().to_bool() { @@ -754,4 +757,39 @@ mod tests { Value::Int(2) ); } + + #[test] + fn lambdas() { + let mut env = ExecEnv::new(); + + // Lambda call from a variable. + eval( + &mut env, + r#" + dim foo 1; + dim to2 λ (var) { 2 =: var; }; + to2(foo); + "#, + ) + .unwrap(); + assert_eq!( + env.get_var(&Spanned { + item: "foo".to_owned(), + span: 1..1, + }) + .unwrap(), + Value::Int(2), + ); + + // Inline lambda call. + eval(&mut env, "(λ (var) { 3 =: var; })(foo);").unwrap(); + assert_eq!( + env.get_var(&Spanned { + item: "foo".to_owned(), + span: 1..1, + }) + .unwrap(), + Value::Int(3), + ); + } } diff --git a/ablescript/src/lexer.rs b/ablescript/src/lexer.rs index e95d663..dc451d5 100644 --- a/ablescript/src/lexer.rs +++ b/ablescript/src/lexer.rs @@ -67,6 +67,14 @@ pub enum Token { #[token("bff")] Bff, + /// Lambda + #[token("λ")] + Lambda, + + /// BFF but lambda + #[token("bfλ")] + BfLambda, + /// Variable bro #[token("dim")] Dim, diff --git a/ablescript/src/parser.rs b/ablescript/src/parser.rs index 5934e0a..9211034 100644 --- a/ablescript/src/parser.rs +++ b/ablescript/src/parser.rs @@ -203,6 +203,16 @@ impl<'source> Parser<'source> { )), }, + // Lambdas + Token::Lambda => Ok(Spanned::new( + self.lambda_flow()?, + start..self.lexer.span().end, + )), + Token::BfLambda => Ok(Spanned::new( + self.bf_lambda_flow()?, + start..self.lexer.span().end, + )), + // Operations Token::Aint if buf.is_none() => Ok(Spanned::new( { @@ -430,12 +440,12 @@ impl<'source> Parser<'source> { }) } - /// Parse functio flow + /// Parse functio body /// - /// functio $ident (a, b, c) { ... } - fn functio_flow(&mut self) -> Result { - let ident = self.get_ident()?; + /// `(a, b, c) { ... }` + fn get_functio_body(&mut self) -> Result, Error> { self.require(Token::LeftParen)?; + let start = self.lexer.span().start; let mut params = vec![]; loop { @@ -462,26 +472,41 @@ impl<'source> Parser<'source> { let body = self.get_block()?; - Ok(Stmt::Functio { - ident, - params, - body, - }) + Ok(Spanned::new( + FunctioBody { params, body }, + start..self.lexer.span().end, + )) } - /// Parse BF function declaration + /// Parse functio flow /// - /// `bff $ident ([tapelen]) { ... }` - fn bff_flow(&mut self) -> Result { + /// functio $ident (a, b, c) { ... } + fn functio_flow(&mut self) -> Result { let ident = self.get_ident()?; + let body = self.get_functio_body()?; - let tape_len = match self.checked_next()? { + Ok(Stmt::Functio { ident, body }) + } + + /// Parse lambda flow + /// + /// `λ (a, b, c) { ... }` + fn lambda_flow(&mut self) -> Result { + Ok(Expr::Lambda(self.get_functio_body()?)) + } + + /// Parse BF function body + /// + /// `([tapelen]) { ... }` + fn get_bff_body(&mut self) -> Result, Error> { + let (tape_len, start) = match self.checked_next()? { Token::LeftParen => { let len = Some(self.expr_flow(Token::RightParen)?); + let start = self.lexer.span().start; self.require(Token::LeftCurly)?; - len + (len, start) } - Token::LeftCurly => None, + Token::LeftCurly => (None, self.lexer.span().start), token => { return Err(Error::new( ErrorKind::UnexpectedToken(token), @@ -505,11 +530,27 @@ impl<'source> Parser<'source> { } } - Ok(Stmt::BfFunctio { - ident, - tape_len, - code, - }) + Ok(Spanned::new( + BfFunctioBody { tape_len, code }, + start..self.lexer.span().end, + )) + } + + /// Parse BF function declaration + /// + /// `bff $ident ([tapelen]) { ... }` + fn bff_flow(&mut self) -> Result { + let ident = self.get_ident()?; + let body = self.get_bff_body()?; + + Ok(Stmt::BfFunctio { ident, body }) + } + + /// Parse a BF lambda + /// + /// `bfλ ([tapelen]) { ... }` + fn bf_lambda_flow(&mut self) -> Result { + Ok(Expr::BfLambda(Box::new(self.get_bff_body()?))) } /// Parse functio call flow @@ -714,6 +755,36 @@ mod tests { assert_eq!(ast, expected); } + #[test] + fn lambda() { + let code = "(λ () { /*ablelambda*/ print; })();"; + let expected = &[Spanned { + item: Stmt::Call { + expr: Spanned { + item: Expr::Lambda(Spanned { + item: FunctioBody { + params: Vec::new(), + body: vec![Spanned { + item: Stmt::Print(Spanned { + item: Expr::Literal(Literal::Str("ablelambda".to_owned())), + span: 9..23, + }), + span: 9..30, + }], + }, + span: 4..32, + }), + span: 1..32, + }, + args: Vec::new(), + }, + span: 0..36, + }]; + + let ast = Parser::new(code).parse().unwrap(); + assert_eq!(ast, expected); + } + #[test] fn tdark() { let code = "T-Dark { dim lang /*lang*/ + lang; }"; diff --git a/examples/lambda.able b/examples/lambda.able new file mode 100644 index 0000000..675ed2c --- /dev/null +++ b/examples/lambda.able @@ -0,0 +1,7 @@ +dim with_world λ (var) { + var + /*world!*/ =: var; +}; + +dim foo /*Hello, */; +with_world(foo); +foo print;