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 {
|
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>>,
|
||||||
|
|
|
@ -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),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
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