forked from AbleScript/ablescript
add lambdas (λ) and brainfuck lambdas (bfλ)
This commit is contained in:
parent
57bbc44b82
commit
c69216082e
|
@ -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>>,
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,14 @@ pub enum Token {
|
|||
#[token("bff")]
|
||||
Bff,
|
||||
|
||||
/// Lambda
|
||||
#[token("λ")]
|
||||
Lambda,
|
||||
|
||||
/// BFF but lambda
|
||||
#[token("bfλ")]
|
||||
BfLambda,
|
||||
|
||||
/// Variable bro
|
||||
#[token("dim")]
|
||||
Dim,
|
||||
|
|
|
@ -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
7
examples/lambda.able
Normal file
|
@ -0,0 +1,7 @@
|
|||
dim with_world λ (var) {
|
||||
var + /*world!*/ =: var;
|
||||
};
|
||||
|
||||
dim foo /*Hello, */;
|
||||
with_world(foo);
|
||||
foo print;
|
Loading…
Reference in a new issue