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

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

View file

@ -124,13 +124,11 @@ pub enum Stmt {
Functio {
ident: Spanned<String>,
params: Vec<Spanned<String>>,
body: Block,
body: Spanned<FunctioBody>,
},
BfFunctio {
ident: Spanned<String>,
tape_len: Option<Spanned<Expr>>,
code: Vec<u8>,
body: Spanned<BfFunctioBody>,
},
Call {
expr: Spanned<Expr>,
@ -143,6 +141,18 @@ pub enum Stmt {
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,
/// like math and logical operations or values.
#[derive(Debug, PartialEq, Clone, Hash)]
@ -155,6 +165,8 @@ pub enum Expr {
Aint(Box<Spanned<Expr>>),
Literal(Literal),
Cart(Vec<(Spanned<Expr>, Spanned<Expr>)>),
Lambda(Spanned<FunctioBody>),
BfLambda(Box<Spanned<BfFunctioBody>>),
Index {
expr: Box<Spanned<Expr>>,
index: Box<Spanned<Expr>>,

View file

@ -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<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.
fn eval_expr(&self, expr: &Spanned<Expr>) -> Result<Value, Error> {
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),
);
}
}

View file

@ -67,6 +67,14 @@ pub enum Token {
#[token("bff")]
Bff,
/// Lambda
#[token("λ")]
Lambda,
/// BFF but lambda
#[token("bfλ")]
BfLambda,
/// Variable bro
#[token("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
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<Stmt, Error> {
let ident = self.get_ident()?;
/// `(a, b, c) { ... }`
fn get_functio_body(&mut self) -> Result<Spanned<FunctioBody>, 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<Stmt, Error> {
/// functio $ident (a, b, c) { ... }
fn functio_flow(&mut self) -> Result<Stmt, Error> {
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 => {
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<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
@ -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; }";

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;