add lambdas (λ) and brainfuck lambdas (bfλ)

This commit is contained in:
wackbyte 2022-04-18 19:57:28 -04:00
parent 57bbc44b82
commit c69216082e
5 changed files with 190 additions and 54 deletions

View file

@ -124,13 +124,11 @@ pub enum Stmt {
Functio { Functio {
ident: Spanned<String>, ident: Spanned<String>,
params: Vec<Spanned<String>>, body: Spanned<FunctioBody>,
body: Block,
}, },
BfFunctio { BfFunctio {
ident: Spanned<String>, ident: Spanned<String>,
tape_len: Option<Spanned<Expr>>, body: Spanned<BfFunctioBody>,
code: Vec<u8>,
}, },
Call { Call {
expr: Spanned<Expr>, expr: Spanned<Expr>,
@ -143,6 +141,18 @@ pub enum Stmt {
Rickroll, Rickroll,
} }
#[derive(Debug, PartialEq, Clone, Hash)]
pub struct FunctioBody {
pub params: Vec<Spanned<String>>,
pub body: Block,
}
#[derive(Debug, PartialEq, Clone, Hash)]
pub struct BfFunctioBody {
pub tape_len: Option<Spanned<Expr>>,
pub code: Vec<u8>,
}
/// Expression is parse unit which do not cause any effect, /// Expression is parse unit which do not cause any effect,
/// like math and logical operations or values. /// like math and logical operations or values.
#[derive(Debug, PartialEq, Clone, Hash)] #[derive(Debug, PartialEq, Clone, Hash)]
@ -155,6 +165,8 @@ pub enum Expr {
Aint(Box<Spanned<Expr>>), Aint(Box<Spanned<Expr>>),
Literal(Literal), Literal(Literal),
Cart(Vec<(Spanned<Expr>, Spanned<Expr>)>), Cart(Vec<(Spanned<Expr>, Spanned<Expr>)>),
Lambda(Spanned<FunctioBody>),
BfLambda(Box<Spanned<BfFunctioBody>>),
Index { Index {
expr: Box<Spanned<Expr>>, expr: Box<Spanned<Expr>>,
index: Box<Spanned<Expr>>, index: Box<Spanned<Expr>>,

View file

@ -9,7 +9,7 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use crate::{ use crate::{
ast::{Assignable, AssignableKind, Expr, Spanned, Stmt}, ast::{Assignable, AssignableKind, BfFunctioBody, Expr, FunctioBody, Spanned, Stmt},
consts::ablescript_consts, consts::ablescript_consts,
error::{Error, ErrorKind}, error::{Error, ErrorKind},
variables::{Functio, Value, ValueRef, Variable}, variables::{Functio, Value, ValueRef, Variable},
@ -152,6 +152,32 @@ impl ExecEnv {
final_result final_result
} }
/// Evaluate a functio body, returning its functio.
fn eval_functio_body(&self, body: &Spanned<FunctioBody>) -> 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<BfFunctioBody>) -> Result<Functio, Error> {
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. /// Evaluate an Expr, returning its value or an error.
fn eval_expr(&self, expr: &Spanned<Expr>) -> Result<Value, Error> { fn eval_expr(&self, expr: &Spanned<Expr>) -> Result<Value, Error> {
use crate::ast::BinOpKind::*; use crate::ast::BinOpKind::*;
@ -174,6 +200,8 @@ impl ExecEnv {
} }
Aint(expr) => !self.eval_expr(expr)?, Aint(expr) => !self.eval_expr(expr)?,
Literal(lit) => lit.clone().into(), 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( Expr::Cart(members) => Value::Cart(
members members
.iter() .iter()
@ -216,36 +244,11 @@ impl ExecEnv {
self.decl_var(&ident.item, init); self.decl_var(&ident.item, init);
} }
Stmt::Functio { Stmt::Functio { ident, body } => {
ident, self.decl_var(&ident.item, Value::Functio(self.eval_functio_body(body)));
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::BfFunctio { Stmt::BfFunctio { ident, body } => {
ident, self.decl_var(&ident.item, Value::Functio(self.eval_bff_body(body)?));
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::Unless { cond, body } => { Stmt::Unless { cond, body } => {
if !self.eval_expr(cond)?.into_abool().to_bool() { if !self.eval_expr(cond)?.into_abool().to_bool() {
@ -754,4 +757,39 @@ mod tests {
Value::Int(2) 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),
);
}
} }

View file

@ -67,6 +67,14 @@ pub enum Token {
#[token("bff")] #[token("bff")]
Bff, Bff,
/// Lambda
#[token("λ")]
Lambda,
/// BFF but lambda
#[token("bfλ")]
BfLambda,
/// Variable bro /// Variable bro
#[token("dim")] #[token("dim")]
Dim, Dim,

View file

@ -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 // Operations
Token::Aint if buf.is_none() => Ok(Spanned::new( 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) { ... } /// `(a, b, c) { ... }`
fn functio_flow(&mut self) -> Result<Stmt, Error> { fn get_functio_body(&mut self) -> Result<Spanned<FunctioBody>, Error> {
let ident = self.get_ident()?;
self.require(Token::LeftParen)?; self.require(Token::LeftParen)?;
let start = self.lexer.span().start;
let mut params = vec![]; let mut params = vec![];
loop { loop {
@ -462,26 +472,41 @@ impl<'source> Parser<'source> {
let body = self.get_block()?; let body = self.get_block()?;
Ok(Stmt::Functio { Ok(Spanned::new(
ident, FunctioBody { params, body },
params, start..self.lexer.span().end,
body, ))
})
} }
/// Parse BF function declaration /// Parse functio flow
/// ///
/// `bff $ident ([tapelen]) { ... }` /// functio $ident (a, b, c) { ... }
fn bff_flow(&mut self) -> Result<Stmt, Error> { fn functio_flow(&mut self) -> Result<Stmt, Error> {
let ident = self.get_ident()?; 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<Expr, Error> {
Ok(Expr::Lambda(self.get_functio_body()?))
}
/// Parse BF function body
///
/// `([tapelen]) { ... }`
fn get_bff_body(&mut self) -> Result<Spanned<BfFunctioBody>, Error> {
let (tape_len, start) = match self.checked_next()? {
Token::LeftParen => { Token::LeftParen => {
let len = Some(self.expr_flow(Token::RightParen)?); let len = Some(self.expr_flow(Token::RightParen)?);
let start = self.lexer.span().start;
self.require(Token::LeftCurly)?; self.require(Token::LeftCurly)?;
len (len, start)
} }
Token::LeftCurly => None, Token::LeftCurly => (None, self.lexer.span().start),
token => { token => {
return Err(Error::new( return Err(Error::new(
ErrorKind::UnexpectedToken(token), ErrorKind::UnexpectedToken(token),
@ -505,11 +530,27 @@ impl<'source> Parser<'source> {
} }
} }
Ok(Stmt::BfFunctio { Ok(Spanned::new(
ident, BfFunctioBody { tape_len, code },
tape_len, start..self.lexer.span().end,
code, ))
}) }
/// Parse BF function declaration
///
/// `bff $ident ([tapelen]) { ... }`
fn bff_flow(&mut self) -> Result<Stmt, Error> {
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<Expr, Error> {
Ok(Expr::BfLambda(Box::new(self.get_bff_body()?)))
} }
/// Parse functio call flow /// Parse functio call flow
@ -714,6 +755,36 @@ mod tests {
assert_eq!(ast, expected); 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] #[test]
fn tdark() { fn tdark() {
let code = "T-Dark { dim lang /*lang*/ + lang; }"; let code = "T-Dark { dim lang /*lang*/ + lang; }";

7
examples/lambda.able Normal file
View file

@ -0,0 +1,7 @@
dim with_world λ (var) {
var + /*world!*/ =: var;
};
dim foo /*Hello, */;
with_world(foo);
foo print;