forked from AbleScript/ablescript
Release 0.2
Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/1
This commit is contained in:
commit
0451e71104
44
able-script-test/by-arity-chain.able
Normal file
44
able-script-test/by-arity-chain.able
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
functio arity_0() {
|
||||||
|
"this function has arity 0" print;
|
||||||
|
}
|
||||||
|
|
||||||
|
functio arity_1(arg1) {
|
||||||
|
"this function has arity 1" print;
|
||||||
|
arg1 print;
|
||||||
|
}
|
||||||
|
|
||||||
|
functio arity_2(arg1, arg2) {
|
||||||
|
"this function has arity 2" print;
|
||||||
|
arg1 print;
|
||||||
|
arg2 print;
|
||||||
|
}
|
||||||
|
|
||||||
|
functio arity_3(arg1, arg2, arg3) {
|
||||||
|
"this function has arity 3" print;
|
||||||
|
arg1 print;
|
||||||
|
arg2 print;
|
||||||
|
arg3 print;
|
||||||
|
}
|
||||||
|
|
||||||
|
owo arity_0();
|
||||||
|
owo arity_1("foo");
|
||||||
|
owo arity_2("foo", "bar");
|
||||||
|
owo arity_3("foo", "bar", "baz");
|
||||||
|
|
||||||
|
var i1 = arity_0 * arity_1;
|
||||||
|
i1("second");
|
||||||
|
|
||||||
|
"----" print;
|
||||||
|
|
||||||
|
var i2 = arity_1 * arity_0;
|
||||||
|
i2("first");
|
||||||
|
|
||||||
|
"----" print;
|
||||||
|
|
||||||
|
var ifancy = arity_3 * arity_3;
|
||||||
|
ifancy("left1", "right1", "left2", "right2", "left3", "right3");
|
||||||
|
|
||||||
|
"----" print;
|
||||||
|
|
||||||
|
var another = arity_0 * arity_3;
|
||||||
|
another("right1", "right2", "right3");
|
|
@ -1,5 +1,5 @@
|
||||||
functio helloable() {
|
functio helloable() {
|
||||||
"Hello, Able!" print;
|
"Hello, Able!" print;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cart = ["able" <= 42, helloable <= "hello"];
|
var cart = ["able" <= 42, helloable <= "hello"];
|
||||||
|
|
|
@ -15,26 +15,71 @@ use crate::variables::Value;
|
||||||
type Span = std::ops::Range<usize>;
|
type Span = std::ops::Range<usize>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Iden {
|
pub struct Ident {
|
||||||
pub iden: String,
|
pub ident: String,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iden {
|
impl Ident {
|
||||||
pub fn new(iden: String, span: Span) -> Self {
|
pub fn new(ident: String, span: Span) -> Self {
|
||||||
Self { iden, span }
|
Self { ident, span }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Iden {
|
impl PartialEq for Ident {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.iden == other.iden
|
self.ident == other.ident
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Iden {
|
impl Hash for Ident {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.iden.hash(state)
|
self.ident.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||||
|
pub struct Assignable {
|
||||||
|
pub ident: Ident,
|
||||||
|
pub kind: AssignableKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||||
|
pub enum AssignableKind {
|
||||||
|
Variable,
|
||||||
|
Index { indices: Vec<Expr> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Assignable {
|
||||||
|
pub fn from_expr(expr: Expr) -> Result<Assignable, ()> {
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Variable(ident) => Ok(Assignable {
|
||||||
|
ident: Ident::new(ident, expr.span),
|
||||||
|
kind: AssignableKind::Variable,
|
||||||
|
}),
|
||||||
|
ExprKind::Index { expr, index } => Self::from_index(*expr, *index),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_index(mut buf: Expr, index: Expr) -> Result<Assignable, ()> {
|
||||||
|
let mut indices = vec![index];
|
||||||
|
let ident = loop {
|
||||||
|
match buf.kind {
|
||||||
|
ExprKind::Variable(ident) => break ident,
|
||||||
|
ExprKind::Index { expr, index } => {
|
||||||
|
indices.push(*index);
|
||||||
|
buf = *expr;
|
||||||
|
}
|
||||||
|
_ => return Err(()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
indices.reverse();
|
||||||
|
Ok(Assignable {
|
||||||
|
ident: Ident::new(ident, buf.span),
|
||||||
|
kind: AssignableKind::Index { indices },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,21 +121,21 @@ pub enum StmtKind {
|
||||||
HopBack,
|
HopBack,
|
||||||
|
|
||||||
Var {
|
Var {
|
||||||
iden: Iden,
|
ident: Ident,
|
||||||
init: Option<Expr>,
|
init: Option<Expr>,
|
||||||
},
|
},
|
||||||
Assign {
|
Assign {
|
||||||
iden: Iden,
|
assignable: Assignable,
|
||||||
value: Expr,
|
value: Expr,
|
||||||
},
|
},
|
||||||
|
|
||||||
Functio {
|
Functio {
|
||||||
iden: Iden,
|
ident: Ident,
|
||||||
params: Vec<Iden>,
|
params: Vec<Ident>,
|
||||||
body: Block,
|
body: Block,
|
||||||
},
|
},
|
||||||
BfFunctio {
|
BfFunctio {
|
||||||
iden: Iden,
|
ident: Ident,
|
||||||
tape_len: Option<Expr>,
|
tape_len: Option<Expr>,
|
||||||
code: Vec<u8>,
|
code: Vec<u8>,
|
||||||
},
|
},
|
||||||
|
@ -99,8 +144,8 @@ pub enum StmtKind {
|
||||||
args: Vec<Expr>,
|
args: Vec<Expr>,
|
||||||
},
|
},
|
||||||
Print(Expr),
|
Print(Expr),
|
||||||
Read(Iden),
|
Read(Assignable),
|
||||||
Melo(Iden),
|
Melo(Ident),
|
||||||
Rlyeh,
|
Rlyeh,
|
||||||
Rickroll,
|
Rickroll,
|
||||||
}
|
}
|
||||||
|
@ -142,7 +187,7 @@ pub enum ExprKind {
|
||||||
Literal(Value),
|
Literal(Value),
|
||||||
Cart(Vec<(Expr, Expr)>),
|
Cart(Vec<(Expr, Expr)>),
|
||||||
Index {
|
Index {
|
||||||
cart: Box<Expr>,
|
expr: Box<Expr>,
|
||||||
index: Box<Expr>,
|
index: Box<Expr>,
|
||||||
},
|
},
|
||||||
Variable(String),
|
Variable(String),
|
||||||
|
@ -164,8 +209,6 @@ pub enum BinOpKind {
|
||||||
Less,
|
Less,
|
||||||
Equal,
|
Equal,
|
||||||
NotEqual,
|
NotEqual,
|
||||||
And,
|
|
||||||
Or,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinOpKind {
|
impl BinOpKind {
|
||||||
|
@ -181,8 +224,6 @@ impl BinOpKind {
|
||||||
Token::LessThan => Ok(Self::Less),
|
Token::LessThan => Ok(Self::Less),
|
||||||
Token::EqualEqual => Ok(Self::Equal),
|
Token::EqualEqual => Ok(Self::Equal),
|
||||||
Token::NotEqual => Ok(Self::NotEqual),
|
Token::NotEqual => Ok(Self::NotEqual),
|
||||||
Token::And => Ok(Self::And),
|
|
||||||
Token::Or => Ok(Self::Or),
|
|
||||||
t => Err(crate::error::ErrorKind::UnexpectedToken(t)),
|
t => Err(crate::error::ErrorKind::UnexpectedToken(t)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,68 +60,6 @@ pub fn char2num(character: char) -> i32 {
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn num2char(number: i32) -> char {
|
|
||||||
match number {
|
|
||||||
-26 => 'Z',
|
|
||||||
-25 => 'Y',
|
|
||||||
-24 => 'X',
|
|
||||||
-23 => 'W',
|
|
||||||
-22 => 'V',
|
|
||||||
-210 => 'U',
|
|
||||||
-20 => 'T',
|
|
||||||
-18 => 'R',
|
|
||||||
-19 => 'S',
|
|
||||||
-17 => 'Q',
|
|
||||||
-16 => 'P',
|
|
||||||
-15 => 'O',
|
|
||||||
-14 => 'N',
|
|
||||||
-13 => 'M',
|
|
||||||
-12 => 'L',
|
|
||||||
-11 => 'K',
|
|
||||||
-10 => 'J',
|
|
||||||
-9 => 'I',
|
|
||||||
-8 => 'H',
|
|
||||||
-7 => 'G',
|
|
||||||
-6 => 'F',
|
|
||||||
-5 => 'E',
|
|
||||||
-4 => 'D',
|
|
||||||
-3 => 'C',
|
|
||||||
-2 => 'B',
|
|
||||||
-1 => 'A',
|
|
||||||
0 => ' ',
|
|
||||||
1 => 'a',
|
|
||||||
2 => 'b',
|
|
||||||
3 => 'c',
|
|
||||||
4 => 'd',
|
|
||||||
5 => 'e',
|
|
||||||
6 => 'f',
|
|
||||||
7 => 'g',
|
|
||||||
8 => 'h',
|
|
||||||
9 => 'i',
|
|
||||||
10 => 'j',
|
|
||||||
11 => 'k',
|
|
||||||
12 => 'l',
|
|
||||||
13 => 'm',
|
|
||||||
14 => 'n',
|
|
||||||
15 => 'o',
|
|
||||||
16 => 'p',
|
|
||||||
17 => 'q',
|
|
||||||
18 => 'r',
|
|
||||||
19 => 's',
|
|
||||||
20 => 't',
|
|
||||||
21 => 'u',
|
|
||||||
22 => 'v',
|
|
||||||
23 => 'w',
|
|
||||||
24 => 'x',
|
|
||||||
25 => 'y',
|
|
||||||
26 => 'z',
|
|
||||||
// NOTE(Able): Why does it jump to 53 here? MY REASONS ARE BEYOND YOUR UNDERSTANDING MORTAL
|
|
||||||
53 => '/',
|
|
||||||
54 => '\\',
|
|
||||||
55 => '.',
|
|
||||||
_ => ' ',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
//! [`interpret_with_output`]: Interpreter::interpret_with_output
|
//! [`interpret_with_output`]: Interpreter::interpret_with_output
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
// Putting this here because we still don't use the entire capabilities of this module. ~~Alex
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
@ -30,6 +32,8 @@ use std::{
|
||||||
/// The default limit for the tape size. This is the value used by methods that don't take it as a parameter
|
/// The default limit for the tape size. This is the value used by methods that don't take it as a parameter
|
||||||
pub const DEFAULT_TAPE_SIZE_LIMIT: usize = 30_000;
|
pub const DEFAULT_TAPE_SIZE_LIMIT: usize = 30_000;
|
||||||
|
|
||||||
|
/// Mappings from integers to BF instructions
|
||||||
|
pub const INSTRUCTION_MAPPINGS: &[u8] = b"[]+-,.<>";
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
/// A brainfuck interpreter. Read the [module level documentation](self) for more
|
/// A brainfuck interpreter. Read the [module level documentation](self) for more
|
||||||
pub struct Interpreter<'a, I> {
|
pub struct Interpreter<'a, I> {
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
cmp::Ordering,
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
io::{stdin, stdout, Read, Write},
|
io::{stdin, stdout, Read, Write},
|
||||||
|
mem::take,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
process::exit,
|
process::exit,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
@ -19,9 +21,9 @@ use std::{
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Expr, ExprKind, Iden, Stmt, StmtKind},
|
ast::{Assignable, AssignableKind, Expr, ExprKind, Ident, Stmt, StmtKind},
|
||||||
base_55,
|
base_55,
|
||||||
consts::{self, ablescript_consts},
|
consts::ablescript_consts,
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
variables::{Functio, Value, Variable},
|
variables::{Functio, Value, Variable},
|
||||||
};
|
};
|
||||||
|
@ -134,66 +136,23 @@ impl ExecEnv {
|
||||||
fn eval_expr(&self, expr: &Expr) -> Result<Value, Error> {
|
fn eval_expr(&self, expr: &Expr) -> Result<Value, Error> {
|
||||||
use crate::ast::BinOpKind::*;
|
use crate::ast::BinOpKind::*;
|
||||||
use crate::ast::ExprKind::*;
|
use crate::ast::ExprKind::*;
|
||||||
use Value::*;
|
|
||||||
|
|
||||||
Ok(match &expr.kind {
|
Ok(match &expr.kind {
|
||||||
BinOp { lhs, rhs, kind } => {
|
BinOp { lhs, rhs, kind } => {
|
||||||
let lhs = self.eval_expr(lhs)?;
|
let lhs = self.eval_expr(lhs)?;
|
||||||
let rhs = self.eval_expr(rhs)?;
|
let rhs = self.eval_expr(rhs)?;
|
||||||
match kind {
|
match kind {
|
||||||
// Arithmetic operators.
|
Add => lhs + rhs,
|
||||||
Add | Subtract | Multiply | Divide => {
|
Subtract => lhs - rhs,
|
||||||
let lhs = lhs.to_i32();
|
Multiply => lhs * rhs,
|
||||||
let rhs = rhs.to_i32();
|
Divide => lhs / rhs,
|
||||||
|
Greater => Value::Bool(lhs > rhs),
|
||||||
let res = match kind {
|
Less => Value::Bool(lhs < rhs),
|
||||||
Add => lhs.checked_add(rhs),
|
Equal => Value::Bool(lhs == rhs),
|
||||||
Subtract => lhs.checked_sub(rhs),
|
NotEqual => Value::Bool(lhs != rhs),
|
||||||
Multiply => lhs.checked_mul(rhs),
|
|
||||||
Divide => lhs.checked_div(rhs),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
.unwrap_or(consts::ANSWER);
|
|
||||||
Int(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Numeric comparisons.
|
|
||||||
Less | Greater => {
|
|
||||||
let lhs = lhs.to_i32();
|
|
||||||
let rhs = rhs.to_i32();
|
|
||||||
|
|
||||||
let res = match kind {
|
|
||||||
Less => lhs < rhs,
|
|
||||||
Greater => lhs > rhs,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
Bool(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// General comparisons.
|
|
||||||
Equal | NotEqual => {
|
|
||||||
let res = match kind {
|
|
||||||
Equal => lhs == rhs,
|
|
||||||
NotEqual => lhs != rhs,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
Bool(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logical connectives.
|
|
||||||
And | Or => {
|
|
||||||
let lhs = lhs.to_bool();
|
|
||||||
let rhs = rhs.to_bool();
|
|
||||||
let res = match kind {
|
|
||||||
And => lhs && rhs,
|
|
||||||
Or => lhs || rhs,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
Bool(res)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Not(expr) => Bool(!self.eval_expr(expr)?.to_bool()),
|
Not(expr) => !self.eval_expr(expr)?,
|
||||||
Literal(value) => value.clone(),
|
Literal(value) => value.clone(),
|
||||||
ExprKind::Cart(members) => Value::Cart(
|
ExprKind::Cart(members) => Value::Cart(
|
||||||
members
|
members
|
||||||
|
@ -206,18 +165,21 @@ impl ExecEnv {
|
||||||
})
|
})
|
||||||
.collect::<Result<HashMap<_, _>, _>>()?,
|
.collect::<Result<HashMap<_, _>, _>>()?,
|
||||||
),
|
),
|
||||||
Index { cart, index } => {
|
Index { expr, index } => {
|
||||||
let cart = self.eval_expr(cart)?;
|
let value = self.eval_expr(expr)?;
|
||||||
let index = self.eval_expr(index)?;
|
let index = self.eval_expr(index)?;
|
||||||
|
|
||||||
// TODO: this probably shouldn't be cloned
|
value
|
||||||
cart.index(&index).borrow().clone()
|
.into_cart()
|
||||||
|
.get(&index)
|
||||||
|
.map(|x| x.borrow().clone())
|
||||||
|
.unwrap_or(Value::Nul)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: not too happy with constructing an artificial
|
// TODO: not too happy with constructing an artificial
|
||||||
// Iden here.
|
// Ident here.
|
||||||
Variable(name) => self.get_var(&Iden {
|
Variable(name) => self.get_var(&Ident {
|
||||||
iden: name.to_owned(),
|
ident: name.to_owned(),
|
||||||
span: expr.span.clone(),
|
span: expr.span.clone(),
|
||||||
})?,
|
})?,
|
||||||
})
|
})
|
||||||
|
@ -229,52 +191,52 @@ impl ExecEnv {
|
||||||
StmtKind::Print(expr) => {
|
StmtKind::Print(expr) => {
|
||||||
println!("{}", self.eval_expr(expr)?);
|
println!("{}", self.eval_expr(expr)?);
|
||||||
}
|
}
|
||||||
StmtKind::Var { iden, init } => {
|
StmtKind::Var { ident, init } => {
|
||||||
let init = match init {
|
let init = match init {
|
||||||
Some(e) => self.eval_expr(e)?,
|
Some(e) => self.eval_expr(e)?,
|
||||||
None => Value::Nul,
|
None => Value::Nul,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.decl_var(&iden.iden, init);
|
self.decl_var(&ident.ident, init);
|
||||||
}
|
}
|
||||||
StmtKind::Functio { iden, params, body } => {
|
StmtKind::Functio {
|
||||||
|
ident,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
self.decl_var(
|
self.decl_var(
|
||||||
&iden.iden,
|
&ident.ident,
|
||||||
Value::Functio(Functio::AbleFunctio {
|
Value::Functio(Functio::Able {
|
||||||
params: params.iter().map(|iden| iden.iden.to_string()).collect(),
|
params: params.iter().map(|ident| ident.ident.to_owned()).collect(),
|
||||||
body: body.block.to_owned(),
|
body: body.block.to_owned(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
StmtKind::BfFunctio {
|
StmtKind::BfFunctio {
|
||||||
iden,
|
ident,
|
||||||
tape_len,
|
tape_len,
|
||||||
code,
|
code,
|
||||||
} => {
|
} => {
|
||||||
self.decl_var(
|
self.decl_var(
|
||||||
&iden.iden,
|
&ident.ident,
|
||||||
Value::Functio(Functio::BfFunctio {
|
Value::Functio(Functio::Bf {
|
||||||
instructions: code.to_owned(),
|
instructions: code.to_owned(),
|
||||||
tape_len: tape_len
|
tape_len: tape_len
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|tape_len| self.eval_expr(tape_len).map(|v| v.to_i32() as usize))
|
.map(|tape_len| self.eval_expr(tape_len).map(|v| v.into_i32() as usize))
|
||||||
.unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?,
|
.unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
StmtKind::If { cond, body } => {
|
StmtKind::If { cond, body } => {
|
||||||
if self.eval_expr(cond)?.to_bool() {
|
if self.eval_expr(cond)?.into_bool() {
|
||||||
return self.eval_stmts_hs(&body.block, true);
|
return self.eval_stmts_hs(&body.block, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::Call { expr, args } => {
|
StmtKind::Call { expr, args } => {
|
||||||
let func = self.eval_expr(expr)?;
|
let func = self.eval_expr(expr)?.into_functio();
|
||||||
|
|
||||||
if let Value::Functio(func) = func {
|
self.fn_call(func, args, &stmt.span)?;
|
||||||
self.fn_call(func, args, &stmt.span)?;
|
|
||||||
} else {
|
|
||||||
// Fail silently for now.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
StmtKind::Loop { body } => loop {
|
StmtKind::Loop { body } => loop {
|
||||||
let res = self.eval_stmts_hs(&body.block, true)?;
|
let res = self.eval_stmts_hs(&body.block, true)?;
|
||||||
|
@ -284,9 +246,8 @@ impl ExecEnv {
|
||||||
HaltStatus::Hopback(_) => continue,
|
HaltStatus::Hopback(_) => continue,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
StmtKind::Assign { iden, value } => {
|
StmtKind::Assign { assignable, value } => {
|
||||||
let value = self.eval_expr(value)?;
|
self.assign(assignable, self.eval_expr(value)?)?;
|
||||||
self.get_var_mut(iden)?.value.replace(value);
|
|
||||||
}
|
}
|
||||||
StmtKind::Break => {
|
StmtKind::Break => {
|
||||||
return Ok(HaltStatus::Break(stmt.span.clone()));
|
return Ok(HaltStatus::Break(stmt.span.clone()));
|
||||||
|
@ -294,8 +255,8 @@ impl ExecEnv {
|
||||||
StmtKind::HopBack => {
|
StmtKind::HopBack => {
|
||||||
return Ok(HaltStatus::Hopback(stmt.span.clone()));
|
return Ok(HaltStatus::Hopback(stmt.span.clone()));
|
||||||
}
|
}
|
||||||
StmtKind::Melo(iden) => {
|
StmtKind::Melo(ident) => {
|
||||||
self.get_var_mut(iden)?.melo = true;
|
self.get_var_mut(ident)?.melo = true;
|
||||||
}
|
}
|
||||||
StmtKind::Rlyeh => {
|
StmtKind::Rlyeh => {
|
||||||
// Maybe print a creepy error message or something
|
// Maybe print a creepy error message or something
|
||||||
|
@ -307,20 +268,69 @@ impl ExecEnv {
|
||||||
.write_all(include_str!("rickroll").as_bytes())
|
.write_all(include_str!("rickroll").as_bytes())
|
||||||
.expect("Failed to write to stdout");
|
.expect("Failed to write to stdout");
|
||||||
}
|
}
|
||||||
StmtKind::Read(iden) => {
|
StmtKind::Read(assignable) => {
|
||||||
let mut value = 0;
|
let mut value = 0;
|
||||||
for _ in 0..READ_BITS {
|
for _ in 0..READ_BITS {
|
||||||
value <<= 1;
|
value <<= 1;
|
||||||
value += self.get_bit()? as i32;
|
value += self.get_bit()? as i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get_var_mut(iden)?.value.replace(Value::Int(value));
|
self.assign(assignable, Value::Int(value))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(HaltStatus::Finished)
|
Ok(HaltStatus::Finished)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assign a value to an Assignable.
|
||||||
|
fn assign(&mut self, dest: &Assignable, value: Value) -> Result<(), Error> {
|
||||||
|
match dest.kind {
|
||||||
|
AssignableKind::Variable => {
|
||||||
|
self.get_var_mut(&dest.ident)?.value.replace(value);
|
||||||
|
}
|
||||||
|
AssignableKind::Index { ref indices } => {
|
||||||
|
let mut cell = self.get_var_rc(&dest.ident)?;
|
||||||
|
for index in indices {
|
||||||
|
let index = self.eval_expr(index)?;
|
||||||
|
|
||||||
|
let next_cell = match &mut *cell.borrow_mut() {
|
||||||
|
Value::Cart(c) => {
|
||||||
|
// cell is a cart, so we can do simple
|
||||||
|
// indexing.
|
||||||
|
if let Some(x) = c.get(&index) {
|
||||||
|
// cell[index] exists, get a shared
|
||||||
|
// reference to it.
|
||||||
|
Rc::clone(x)
|
||||||
|
} else {
|
||||||
|
// cell[index] does not exist, so we
|
||||||
|
// insert an empty cart by default
|
||||||
|
// instead.
|
||||||
|
let next_cell =
|
||||||
|
Rc::new(RefCell::new(Value::Cart(Default::default())));
|
||||||
|
c.insert(index, Rc::clone(&next_cell));
|
||||||
|
next_cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
// cell is not a cart; `take` it, convert
|
||||||
|
// it into a cart, and write the result
|
||||||
|
// back into it.
|
||||||
|
let mut cart = take(x).into_cart();
|
||||||
|
let next_cell = Rc::new(RefCell::new(Value::Cart(Default::default())));
|
||||||
|
cart.insert(index, Rc::clone(&next_cell));
|
||||||
|
*x = Value::Cart(cart);
|
||||||
|
next_cell
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cell = next_cell;
|
||||||
|
}
|
||||||
|
cell.replace(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Call a function with the given arguments (i.e., actual
|
/// Call a function with the given arguments (i.e., actual
|
||||||
/// parameters). If the function invocation fails for some reason,
|
/// parameters). If the function invocation fails for some reason,
|
||||||
/// report the error at `span`.
|
/// report the error at `span`.
|
||||||
|
@ -331,8 +341,8 @@ impl ExecEnv {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| {
|
.map(|arg| {
|
||||||
if let ExprKind::Variable(name) = &arg.kind {
|
if let ExprKind::Variable(name) = &arg.kind {
|
||||||
self.get_var_rc(&Iden {
|
self.get_var_rc(&Ident {
|
||||||
iden: name.to_owned(),
|
ident: name.to_owned(),
|
||||||
span: arg.span.clone(),
|
span: arg.span.clone(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -341,8 +351,17 @@ impl ExecEnv {
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, Error>>()?;
|
.collect::<Result<Vec<_>, Error>>()?;
|
||||||
|
|
||||||
|
self.fn_call_with_values(func, &args, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_call_with_values(
|
||||||
|
&mut self,
|
||||||
|
func: Functio,
|
||||||
|
args: &[Rc<RefCell<Value>>],
|
||||||
|
span: &Range<usize>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
match func {
|
match func {
|
||||||
Functio::BfFunctio {
|
Functio::Bf {
|
||||||
instructions,
|
instructions,
|
||||||
tape_len,
|
tape_len,
|
||||||
} => {
|
} => {
|
||||||
|
@ -368,7 +387,7 @@ impl ExecEnv {
|
||||||
.write_all(&output)
|
.write_all(&output)
|
||||||
.expect("Failed to write to stdout");
|
.expect("Failed to write to stdout");
|
||||||
}
|
}
|
||||||
Functio::AbleFunctio { params, body } => {
|
Functio::Able { params, body } => {
|
||||||
if params.len() != args.len() {
|
if params.len() != args.len() {
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
kind: ErrorKind::MismatchedArgumentError,
|
kind: ErrorKind::MismatchedArgumentError,
|
||||||
|
@ -387,10 +406,74 @@ impl ExecEnv {
|
||||||
self.stack.pop();
|
self.stack.pop();
|
||||||
res?;
|
res?;
|
||||||
}
|
}
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (left_functio, right_functio) = *functios;
|
||||||
|
match kind {
|
||||||
|
crate::variables::FunctioChainKind::Equal => {
|
||||||
|
let (l, r) = args.split_at(args.len() / 2);
|
||||||
|
|
||||||
|
self.fn_call_with_values(left_functio, l, span)?;
|
||||||
|
self.fn_call_with_values(right_functio, r, span)?;
|
||||||
|
}
|
||||||
|
crate::variables::FunctioChainKind::ByArity => {
|
||||||
|
let (l, r) =
|
||||||
|
Self::deinterlace(args, (left_functio.arity(), right_functio.arity()));
|
||||||
|
|
||||||
|
self.fn_call_with_values(left_functio, &l, span)?;
|
||||||
|
self.fn_call_with_values(right_functio, &r, span)?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Functio::Eval(code) => {
|
||||||
|
if !args.is_empty() {
|
||||||
|
return Err(Error {
|
||||||
|
kind: ErrorKind::MismatchedArgumentError,
|
||||||
|
span: span.to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let stmts = crate::parser::Parser::new(&code).init()?;
|
||||||
|
self.eval_stmts(&stmts)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deinterlace(
|
||||||
|
args: &[Rc<RefCell<Value>>],
|
||||||
|
arities: (usize, usize),
|
||||||
|
) -> (Vec<Rc<RefCell<Value>>>, Vec<Rc<RefCell<Value>>>) {
|
||||||
|
let n_alternations = usize::min(arities.0, arities.1);
|
||||||
|
let (extra_l, extra_r) = match Ord::cmp(&arities.0, &arities.1) {
|
||||||
|
Ordering::Less => (0, arities.1 - arities.0),
|
||||||
|
Ordering::Equal => (0, 0),
|
||||||
|
Ordering::Greater => (arities.0 - arities.1, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
args.chunks(2)
|
||||||
|
.take(n_alternations)
|
||||||
|
.map(|chunk| Rc::clone(&chunk[0]))
|
||||||
|
.chain(
|
||||||
|
args[2 * n_alternations..]
|
||||||
|
.iter()
|
||||||
|
.map(|r| Rc::clone(r))
|
||||||
|
.take(extra_l),
|
||||||
|
)
|
||||||
|
.collect(),
|
||||||
|
args.chunks(2)
|
||||||
|
.take(n_alternations)
|
||||||
|
.map(|chunk| Rc::clone(&chunk[1]))
|
||||||
|
.chain(
|
||||||
|
args[2 * n_alternations..]
|
||||||
|
.iter()
|
||||||
|
.map(|r| Rc::clone(r))
|
||||||
|
.take(extra_r),
|
||||||
|
)
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a single bit from the bit buffer, or refill it from
|
/// Get a single bit from the bit buffer, or refill it from
|
||||||
/// standard input if it is empty.
|
/// standard input if it is empty.
|
||||||
fn get_bit(&mut self) -> Result<bool, Error> {
|
fn get_bit(&mut self) -> Result<bool, Error> {
|
||||||
|
@ -413,9 +496,9 @@ impl ExecEnv {
|
||||||
|
|
||||||
/// Get the value of a variable. Throw an error if the variable is
|
/// Get the value of a variable. Throw an error if the variable is
|
||||||
/// inaccessible or banned.
|
/// inaccessible or banned.
|
||||||
fn get_var(&self, name: &Iden) -> Result<Value, Error> {
|
fn get_var(&self, name: &Ident) -> Result<Value, Error> {
|
||||||
// One-letter names are reserved as base55 numbers.
|
// One-letter names are reserved as base55 numbers.
|
||||||
let mut chars = name.iden.chars();
|
let mut chars = name.ident.chars();
|
||||||
if let (Some(first), None) = (chars.next(), chars.next()) {
|
if let (Some(first), None) = (chars.next(), chars.next()) {
|
||||||
return Ok(Value::Int(base_55::char2num(first)));
|
return Ok(Value::Int(base_55::char2num(first)));
|
||||||
}
|
}
|
||||||
|
@ -426,20 +509,20 @@ impl ExecEnv {
|
||||||
.stack
|
.stack
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|scope| scope.variables.get(&name.iden))
|
.find_map(|scope| scope.variables.get(&name.ident))
|
||||||
{
|
{
|
||||||
Some(var) => {
|
Some(var) => {
|
||||||
if !var.melo {
|
if !var.melo {
|
||||||
Ok(var.value.borrow().clone())
|
Ok(var.value.borrow().clone())
|
||||||
} else {
|
} else {
|
||||||
Err(Error {
|
Err(Error {
|
||||||
kind: ErrorKind::MeloVariable(name.iden.to_owned()),
|
kind: ErrorKind::MeloVariable(name.ident.to_owned()),
|
||||||
span: name.span.clone(),
|
span: name.span.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => Err(Error {
|
None => Err(Error {
|
||||||
kind: ErrorKind::UnknownVariable(name.iden.to_owned()),
|
kind: ErrorKind::UnknownVariable(name.ident.to_owned()),
|
||||||
span: name.span.clone(),
|
span: name.span.clone(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -447,27 +530,27 @@ impl ExecEnv {
|
||||||
|
|
||||||
/// Get a mutable reference to a variable. Throw an error if the
|
/// Get a mutable reference to a variable. Throw an error if the
|
||||||
/// variable is inaccessible or banned.
|
/// variable is inaccessible or banned.
|
||||||
fn get_var_mut(&mut self, name: &Iden) -> Result<&mut Variable, Error> {
|
fn get_var_mut(&mut self, name: &Ident) -> Result<&mut Variable, Error> {
|
||||||
// This function has a lot of duplicated code with `get_var`,
|
// This function has a lot of duplicated code with `get_var`,
|
||||||
// which I feel like is a bad sign...
|
// which I feel like is a bad sign...
|
||||||
match self
|
match self
|
||||||
.stack
|
.stack
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|scope| scope.variables.get_mut(&name.iden))
|
.find_map(|scope| scope.variables.get_mut(&name.ident))
|
||||||
{
|
{
|
||||||
Some(var) => {
|
Some(var) => {
|
||||||
if !var.melo {
|
if !var.melo {
|
||||||
Ok(var)
|
Ok(var)
|
||||||
} else {
|
} else {
|
||||||
Err(Error {
|
Err(Error {
|
||||||
kind: ErrorKind::MeloVariable(name.iden.to_owned()),
|
kind: ErrorKind::MeloVariable(name.ident.to_owned()),
|
||||||
span: name.span.clone(),
|
span: name.span.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => Err(Error {
|
None => Err(Error {
|
||||||
kind: ErrorKind::UnknownVariable(name.iden.to_owned()),
|
kind: ErrorKind::UnknownVariable(name.ident.to_owned()),
|
||||||
span: name.span.clone(),
|
span: name.span.clone(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -475,7 +558,7 @@ impl ExecEnv {
|
||||||
|
|
||||||
/// Get an Rc'd pointer to the value of a variable. Throw an error
|
/// Get an Rc'd pointer to the value of a variable. Throw an error
|
||||||
/// if the variable is inaccessible or banned.
|
/// if the variable is inaccessible or banned.
|
||||||
fn get_var_rc(&mut self, name: &Iden) -> Result<Rc<RefCell<Value>>, Error> {
|
fn get_var_rc(&mut self, name: &Ident) -> Result<Rc<RefCell<Value>>, Error> {
|
||||||
Ok(self.get_var_mut(name)?.value.clone())
|
Ok(self.get_var_mut(name)?.value.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,7 +654,7 @@ mod tests {
|
||||||
span: 1..1
|
span: 1..1
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Value::Int(42)
|
Value::Int(-2147483648)
|
||||||
);
|
);
|
||||||
|
|
||||||
// And the same for divide by zero.
|
// And the same for divide by zero.
|
||||||
|
@ -579,7 +662,7 @@ mod tests {
|
||||||
env.eval_expr(&Expr {
|
env.eval_expr(&Expr {
|
||||||
kind: ExprKind::BinOp {
|
kind: ExprKind::BinOp {
|
||||||
lhs: Box::new(Expr {
|
lhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Int(1)),
|
kind: ExprKind::Literal(Value::Int(84)),
|
||||||
span: 1..1,
|
span: 1..1,
|
||||||
}),
|
}),
|
||||||
rhs: Box::new(Expr {
|
rhs: Box::new(Expr {
|
||||||
|
@ -591,7 +674,7 @@ mod tests {
|
||||||
span: 1..1
|
span: 1..1
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Value::Int(42)
|
Value::Int(2)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,8 +700,8 @@ mod tests {
|
||||||
// Declaring and reading from a variable.
|
// Declaring and reading from a variable.
|
||||||
eval(&mut env, "var foo = 32; var bar = foo + 1;").unwrap();
|
eval(&mut env, "var foo = 32; var bar = foo + 1;").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.get_var(&Iden {
|
env.get_var(&Ident {
|
||||||
iden: "bar".to_owned(),
|
ident: "bar".to_owned(),
|
||||||
span: 1..1,
|
span: 1..1,
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -628,8 +711,8 @@ mod tests {
|
||||||
// Assigning an existing variable.
|
// Assigning an existing variable.
|
||||||
eval(&mut env, "foo = \"hi\";").unwrap();
|
eval(&mut env, "foo = \"hi\";").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.get_var(&Iden {
|
env.get_var(&Ident {
|
||||||
iden: "foo".to_owned(),
|
ident: "foo".to_owned(),
|
||||||
span: 1..1,
|
span: 1..1,
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -654,8 +737,8 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
env.get_var(&Iden {
|
env.get_var(&Ident {
|
||||||
iden: "foo".to_owned(),
|
ident: "foo".to_owned(),
|
||||||
span: 1..1,
|
span: 1..1,
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
|
|
@ -64,13 +64,7 @@ pub enum Token {
|
||||||
#[token("!=")]
|
#[token("!=")]
|
||||||
NotEqual,
|
NotEqual,
|
||||||
|
|
||||||
#[token("&")]
|
#[regex("!|aint")] // also add aint as a not keyword
|
||||||
And,
|
|
||||||
|
|
||||||
#[token("|")]
|
|
||||||
Or,
|
|
||||||
|
|
||||||
#[token("!|aint")] // also add aint as a not keyword
|
|
||||||
Not,
|
Not,
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
|
@ -123,7 +117,7 @@ pub enum Token {
|
||||||
|
|
||||||
// Literals
|
// Literals
|
||||||
/// True, False
|
/// True, False
|
||||||
#[regex("true|false", get_bool)]
|
#[regex("true|false", get_value)]
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
|
||||||
/// Always, Sometimes, Never
|
/// Always, Sometimes, Never
|
||||||
|
@ -139,11 +133,11 @@ pub enum Token {
|
||||||
String(String),
|
String(String),
|
||||||
|
|
||||||
/// Integer
|
/// Integer
|
||||||
#[regex(r"-?[0-9]+", get_int)]
|
#[regex(r"-?[0-9]+", get_value)]
|
||||||
Integer(i32),
|
Integer(i32),
|
||||||
|
|
||||||
/// A C-complaint identifier
|
/// A C-complaint identifier
|
||||||
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)]
|
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_ident)]
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
|
|
||||||
#[regex(r"owo .*")]
|
#[regex(r"owo .*")]
|
||||||
|
@ -157,11 +151,7 @@ pub enum Token {
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bool(lexer: &mut Lexer<Token>) -> Option<bool> {
|
fn get_value<T: std::str::FromStr>(lexer: &mut Lexer<Token>) -> Option<T> {
|
||||||
lexer.slice().parse().ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_int(lexer: &mut Lexer<Token>) -> Option<i32> {
|
|
||||||
lexer.slice().parse().ok()
|
lexer.slice().parse().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +168,7 @@ fn get_abool(lexer: &mut Lexer<Token>) -> Option<Abool> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_iden(lexer: &mut Lexer<Token>) -> String {
|
fn get_ident(lexer: &mut Lexer<Token>) -> String {
|
||||||
lexer.slice().to_owned()
|
lexer.slice().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,12 +37,7 @@ impl<'source> Parser<'source> {
|
||||||
Token::Comment => continue,
|
Token::Comment => continue,
|
||||||
|
|
||||||
// T-Dark block (replace `lang` with `script`)
|
// T-Dark block (replace `lang` with `script`)
|
||||||
Token::TDark => {
|
Token::TDark => ast.extend(self.tdark_flow()?.block),
|
||||||
self.tdark = true;
|
|
||||||
let mut block = self.get_block()?;
|
|
||||||
ast.append(&mut block.block);
|
|
||||||
self.tdark = false;
|
|
||||||
}
|
|
||||||
token => ast.push(self.parse(token)?),
|
token => ast.push(self.parse(token)?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,9 +48,16 @@ impl<'source> Parser<'source> {
|
||||||
///
|
///
|
||||||
/// If EOF, return Error instead of None
|
/// If EOF, return Error instead of None
|
||||||
fn checked_next(&mut self) -> Result<Token, Error> {
|
fn checked_next(&mut self) -> Result<Token, Error> {
|
||||||
self.lexer
|
loop {
|
||||||
.next()
|
match self
|
||||||
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))
|
.lexer
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?
|
||||||
|
{
|
||||||
|
Token::Comment => (),
|
||||||
|
token => break Ok(token),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a token
|
/// Parse a token
|
||||||
|
@ -76,19 +78,19 @@ impl<'source> Parser<'source> {
|
||||||
Token::Melo => Ok(Stmt::new(self.melo_flow()?, start..self.lexer.span().end)),
|
Token::Melo => Ok(Stmt::new(self.melo_flow()?, start..self.lexer.span().end)),
|
||||||
Token::Loop => Ok(Stmt::new(self.loop_flow()?, start..self.lexer.span().end)),
|
Token::Loop => Ok(Stmt::new(self.loop_flow()?, start..self.lexer.span().end)),
|
||||||
Token::Break => Ok(Stmt::new(
|
Token::Break => Ok(Stmt::new(
|
||||||
self.semi_terminated(StmtKind::Break)?,
|
self.semicolon_terminated(StmtKind::Break)?,
|
||||||
start..self.lexer.span().end,
|
start..self.lexer.span().end,
|
||||||
)),
|
)),
|
||||||
Token::HopBack => Ok(Stmt::new(
|
Token::HopBack => Ok(Stmt::new(
|
||||||
self.semi_terminated(StmtKind::HopBack)?,
|
self.semicolon_terminated(StmtKind::HopBack)?,
|
||||||
start..self.lexer.span().end,
|
start..self.lexer.span().end,
|
||||||
)),
|
)),
|
||||||
Token::Rlyeh => Ok(Stmt::new(
|
Token::Rlyeh => Ok(Stmt::new(
|
||||||
self.semi_terminated(StmtKind::Rlyeh)?,
|
self.semicolon_terminated(StmtKind::Rlyeh)?,
|
||||||
start..self.lexer.span().end,
|
start..self.lexer.span().end,
|
||||||
)),
|
)),
|
||||||
Token::Rickroll => Ok(Stmt::new(
|
Token::Rickroll => Ok(Stmt::new(
|
||||||
self.semi_terminated(StmtKind::Rickroll)?,
|
self.semicolon_terminated(StmtKind::Rickroll)?,
|
||||||
start..self.lexer.span().end,
|
start..self.lexer.span().end,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
|
@ -98,6 +100,8 @@ impl<'source> Parser<'source> {
|
||||||
| Token::Integer(_)
|
| Token::Integer(_)
|
||||||
| Token::Abool(_)
|
| Token::Abool(_)
|
||||||
| Token::Bool(_)
|
| Token::Bool(_)
|
||||||
|
| Token::Nul
|
||||||
|
| Token::Not
|
||||||
| Token::LeftBracket
|
| Token::LeftBracket
|
||||||
| Token::LeftParen => Ok(Stmt::new(
|
| Token::LeftParen => Ok(Stmt::new(
|
||||||
self.value_flow(token)?,
|
self.value_flow(token)?,
|
||||||
|
@ -114,7 +118,7 @@ impl<'source> Parser<'source> {
|
||||||
/// Require statement to be semicolon terminated
|
/// Require statement to be semicolon terminated
|
||||||
///
|
///
|
||||||
/// Utility function for short statements
|
/// Utility function for short statements
|
||||||
fn semi_terminated(&mut self, stmt_kind: StmtKind) -> Result<StmtKind, Error> {
|
fn semicolon_terminated(&mut self, stmt_kind: StmtKind) -> Result<StmtKind, Error> {
|
||||||
self.require(Token::Semicolon)?;
|
self.require(Token::Semicolon)?;
|
||||||
Ok(stmt_kind)
|
Ok(stmt_kind)
|
||||||
}
|
}
|
||||||
|
@ -129,13 +133,13 @@ impl<'source> Parser<'source> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an Identifier
|
/// Get an Identifier
|
||||||
fn get_iden(&mut self) -> Result<Iden, Error> {
|
fn get_ident(&mut self) -> Result<Ident, Error> {
|
||||||
match self.checked_next()? {
|
match self.checked_next()? {
|
||||||
Token::Identifier(iden) => Ok(Iden {
|
Token::Identifier(ident) => Ok(Ident {
|
||||||
iden: if self.tdark {
|
ident: if self.tdark {
|
||||||
iden.replace("lang", "script")
|
ident.replace("lang", "script")
|
||||||
} else {
|
} else {
|
||||||
iden
|
ident
|
||||||
},
|
},
|
||||||
span: self.lexer.span(),
|
span: self.lexer.span(),
|
||||||
}),
|
}),
|
||||||
|
@ -192,7 +196,7 @@ impl<'source> Parser<'source> {
|
||||||
Token::LeftBracket => match buf.take() {
|
Token::LeftBracket => match buf.take() {
|
||||||
Some(buf) => Ok(Expr::new(
|
Some(buf) => Ok(Expr::new(
|
||||||
ExprKind::Index {
|
ExprKind::Index {
|
||||||
cart: Box::new(buf),
|
expr: Box::new(buf),
|
||||||
index: Box::new(self.expr_flow(Token::RightBracket)?),
|
index: Box::new(self.expr_flow(Token::RightBracket)?),
|
||||||
},
|
},
|
||||||
start..self.lexer.span().end,
|
start..self.lexer.span().end,
|
||||||
|
@ -208,9 +212,7 @@ impl<'source> Parser<'source> {
|
||||||
| Token::EqualEqual
|
| Token::EqualEqual
|
||||||
| Token::NotEqual
|
| Token::NotEqual
|
||||||
| Token::LessThan
|
| Token::LessThan
|
||||||
| Token::GreaterThan
|
| Token::GreaterThan => Ok(Expr::new(
|
||||||
| Token::And
|
|
||||||
| Token::Or => Ok(Expr::new(
|
|
||||||
self.binop_flow(
|
self.binop_flow(
|
||||||
BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?,
|
BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?,
|
||||||
buf,
|
buf,
|
||||||
|
@ -322,12 +324,21 @@ impl<'source> Parser<'source> {
|
||||||
loop {
|
loop {
|
||||||
match self.checked_next()? {
|
match self.checked_next()? {
|
||||||
Token::RightCurly => break,
|
Token::RightCurly => break,
|
||||||
|
Token::TDark => block.extend(self.tdark_flow()?.block),
|
||||||
t => block.push(self.parse(t)?),
|
t => block.push(self.parse(t)?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Block { block })
|
Ok(Block { block })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse T-Dark block
|
||||||
|
fn tdark_flow(&mut self) -> Result<Block, Error> {
|
||||||
|
self.tdark = true;
|
||||||
|
let block = self.get_block();
|
||||||
|
self.tdark = false;
|
||||||
|
block
|
||||||
|
}
|
||||||
|
|
||||||
/// If Statement parser gets any kind of value (Identifier or Literal)
|
/// If Statement parser gets any kind of value (Identifier or Literal)
|
||||||
/// It cannot parse it as it do not parse expressions. Instead of it it
|
/// It cannot parse it as it do not parse expressions. Instead of it it
|
||||||
/// will parse it to function call or print statement.
|
/// will parse it to function call or print statement.
|
||||||
|
@ -340,7 +351,7 @@ impl<'source> Parser<'source> {
|
||||||
let stmt = StmtKind::Print(buf.take().ok_or_else(|| {
|
let stmt = StmtKind::Print(buf.take().ok_or_else(|| {
|
||||||
Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span())
|
Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span())
|
||||||
})?);
|
})?);
|
||||||
break self.semi_terminated(stmt)?;
|
break self.semicolon_terminated(stmt)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functio call
|
// Functio call
|
||||||
|
@ -355,26 +366,29 @@ impl<'source> Parser<'source> {
|
||||||
|
|
||||||
// Variable Assignment
|
// Variable Assignment
|
||||||
Token::Equal => {
|
Token::Equal => {
|
||||||
if let Some(Expr {
|
if let Some(Ok(assignable)) = buf.take().map(Assignable::from_expr) {
|
||||||
kind: ExprKind::Variable(iden),
|
|
||||||
span,
|
|
||||||
}) = buf
|
|
||||||
{
|
|
||||||
break StmtKind::Assign {
|
break StmtKind::Assign {
|
||||||
iden: Iden::new(iden, span),
|
assignable,
|
||||||
value: self.expr_flow(Token::Semicolon)?,
|
value: self.expr_flow(Token::Semicolon)?,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::UnexpectedToken(Token::Equal),
|
||||||
|
self.lexer.span(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read input
|
// Read input
|
||||||
Token::Read => {
|
Token::Read => {
|
||||||
if let Some(Expr {
|
if let Some(Ok(assignable)) = buf.take().map(Assignable::from_expr) {
|
||||||
kind: ExprKind::Variable(iden),
|
self.require(Token::Semicolon)?;
|
||||||
span,
|
break StmtKind::Read(assignable);
|
||||||
}) = buf
|
} else {
|
||||||
{
|
return Err(Error::new(
|
||||||
break self.semi_terminated(StmtKind::Read(Iden::new(iden, span)))?;
|
ErrorKind::UnexpectedToken(Token::Read),
|
||||||
|
self.lexer.span(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,9 +414,9 @@ impl<'source> Parser<'source> {
|
||||||
|
|
||||||
/// Parse functio flow
|
/// Parse functio flow
|
||||||
///
|
///
|
||||||
/// functio $iden (a, b, c) { ... }
|
/// functio $ident (a, b, c) { ... }
|
||||||
fn functio_flow(&mut self) -> Result<StmtKind, Error> {
|
fn functio_flow(&mut self) -> Result<StmtKind, Error> {
|
||||||
let iden = self.get_iden()?;
|
let ident = self.get_ident()?;
|
||||||
|
|
||||||
self.require(Token::LeftParen)?;
|
self.require(Token::LeftParen)?;
|
||||||
|
|
||||||
|
@ -411,7 +425,7 @@ impl<'source> Parser<'source> {
|
||||||
match self.checked_next()? {
|
match self.checked_next()? {
|
||||||
Token::RightParen => break,
|
Token::RightParen => break,
|
||||||
Token::Identifier(i) => {
|
Token::Identifier(i) => {
|
||||||
params.push(Iden::new(i, self.lexer.span()));
|
params.push(Ident::new(i, self.lexer.span()));
|
||||||
|
|
||||||
// Require comma (next) or right paren (end) after identifier
|
// Require comma (next) or right paren (end) after identifier
|
||||||
match self.checked_next()? {
|
match self.checked_next()? {
|
||||||
|
@ -431,14 +445,18 @@ impl<'source> Parser<'source> {
|
||||||
|
|
||||||
let body = self.get_block()?;
|
let body = self.get_block()?;
|
||||||
|
|
||||||
Ok(StmtKind::Functio { iden, params, body })
|
Ok(StmtKind::Functio {
|
||||||
|
ident,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse BF function declaration
|
/// Parse BF function declaration
|
||||||
///
|
///
|
||||||
/// `bff $iden ([tapelen]) { ... }`
|
/// `bff $ident ([tapelen]) { ... }`
|
||||||
fn bff_flow(&mut self) -> Result<StmtKind, Error> {
|
fn bff_flow(&mut self) -> Result<StmtKind, Error> {
|
||||||
let iden = self.get_iden()?;
|
let ident = self.get_ident()?;
|
||||||
|
|
||||||
let tape_len = match self.checked_next()? {
|
let tape_len = match self.checked_next()? {
|
||||||
Token::LeftParen => {
|
Token::LeftParen => {
|
||||||
|
@ -472,7 +490,7 @@ impl<'source> Parser<'source> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(StmtKind::BfFunctio {
|
Ok(StmtKind::BfFunctio {
|
||||||
iden,
|
ident,
|
||||||
tape_len,
|
tape_len,
|
||||||
code,
|
code,
|
||||||
})
|
})
|
||||||
|
@ -513,20 +531,20 @@ impl<'source> Parser<'source> {
|
||||||
|
|
||||||
/// Parse variable declaration
|
/// Parse variable declaration
|
||||||
fn var_flow(&mut self) -> Result<StmtKind, Error> {
|
fn var_flow(&mut self) -> Result<StmtKind, Error> {
|
||||||
let iden = self.get_iden()?;
|
let ident = self.get_ident()?;
|
||||||
let init = match self.checked_next()? {
|
let init = match self.checked_next()? {
|
||||||
Token::Equal => Some(self.expr_flow(Token::Semicolon)?),
|
Token::Equal => Some(self.expr_flow(Token::Semicolon)?),
|
||||||
Token::Semicolon => None,
|
Token::Semicolon => None,
|
||||||
t => return Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
|
t => return Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(StmtKind::Var { iden, init })
|
Ok(StmtKind::Var { ident, init })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Melo flow
|
/// Parse Melo flow
|
||||||
fn melo_flow(&mut self) -> Result<StmtKind, Error> {
|
fn melo_flow(&mut self) -> Result<StmtKind, Error> {
|
||||||
let iden = self.get_iden()?;
|
let ident = self.get_ident()?;
|
||||||
self.semi_terminated(StmtKind::Melo(iden))
|
self.semicolon_terminated(StmtKind::Melo(ident))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse loop flow
|
/// Parse loop flow
|
||||||
|
@ -558,7 +576,7 @@ mod tests {
|
||||||
rhs: Box::new(Expr {
|
rhs: Box::new(Expr {
|
||||||
kind: ExprKind::BinOp {
|
kind: ExprKind::BinOp {
|
||||||
lhs: Box::new(Expr {
|
lhs: Box::new(Expr {
|
||||||
kind: ExprKind::Variable("a".to_string()),
|
kind: ExprKind::Variable("a".to_owned()),
|
||||||
span: 5..6,
|
span: 5..6,
|
||||||
}),
|
}),
|
||||||
rhs: Box::new(Expr {
|
rhs: Box::new(Expr {
|
||||||
|
@ -593,8 +611,8 @@ mod tests {
|
||||||
let code = r#"var a = 42;"#;
|
let code = r#"var a = 42;"#;
|
||||||
let expected = &[Stmt {
|
let expected = &[Stmt {
|
||||||
kind: StmtKind::Var {
|
kind: StmtKind::Var {
|
||||||
iden: Iden {
|
ident: Ident {
|
||||||
iden: "a".to_string(),
|
ident: "a".to_owned(),
|
||||||
span: 4..5,
|
span: 4..5,
|
||||||
},
|
},
|
||||||
init: Some(Expr {
|
init: Some(Expr {
|
||||||
|
@ -631,7 +649,7 @@ mod tests {
|
||||||
body: Block {
|
body: Block {
|
||||||
block: vec![Stmt {
|
block: vec![Stmt {
|
||||||
kind: StmtKind::Print(Expr {
|
kind: StmtKind::Print(Expr {
|
||||||
kind: ExprKind::Literal(Value::Str("Buy Able products!".to_string())),
|
kind: ExprKind::Literal(Value::Str("Buy Able products!".to_owned())),
|
||||||
span: 19..39,
|
span: 19..39,
|
||||||
}),
|
}),
|
||||||
span: 19..46,
|
span: 19..46,
|
||||||
|
@ -650,18 +668,18 @@ mod tests {
|
||||||
let code = r#"T-Dark { var lang = "lang" + lang; }"#;
|
let code = r#"T-Dark { var lang = "lang" + lang; }"#;
|
||||||
let expected = &[Stmt {
|
let expected = &[Stmt {
|
||||||
kind: StmtKind::Var {
|
kind: StmtKind::Var {
|
||||||
iden: Iden {
|
ident: Ident {
|
||||||
iden: "script".to_string(),
|
ident: "script".to_owned(),
|
||||||
span: 13..17,
|
span: 13..17,
|
||||||
},
|
},
|
||||||
init: Some(Expr {
|
init: Some(Expr {
|
||||||
kind: ExprKind::BinOp {
|
kind: ExprKind::BinOp {
|
||||||
lhs: Box::new(Expr {
|
lhs: Box::new(Expr {
|
||||||
kind: ExprKind::Literal(Value::Str("script".to_string())),
|
kind: ExprKind::Literal(Value::Str("script".to_owned())),
|
||||||
span: 20..26,
|
span: 20..26,
|
||||||
}),
|
}),
|
||||||
rhs: Box::new(Expr {
|
rhs: Box::new(Expr {
|
||||||
kind: ExprKind::Variable("script".to_string()),
|
kind: ExprKind::Variable("script".to_owned()),
|
||||||
span: 29..33,
|
span: 29..33,
|
||||||
}),
|
}),
|
||||||
kind: BinOpKind::Add,
|
kind: BinOpKind::Add,
|
||||||
|
@ -684,7 +702,7 @@ mod tests {
|
||||||
kind: ExprKind::Cart(vec![
|
kind: ExprKind::Cart(vec![
|
||||||
(
|
(
|
||||||
Expr {
|
Expr {
|
||||||
kind: ExprKind::Literal(Value::Str("able".to_string())),
|
kind: ExprKind::Literal(Value::Str("able".to_owned())),
|
||||||
span: 1..7,
|
span: 1..7,
|
||||||
},
|
},
|
||||||
Expr {
|
Expr {
|
||||||
|
@ -694,7 +712,7 @@ mod tests {
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Expr {
|
Expr {
|
||||||
kind: ExprKind::Literal(Value::Str("script".to_string())),
|
kind: ExprKind::Literal(Value::Str("script".to_owned())),
|
||||||
span: 14..22,
|
span: 14..22,
|
||||||
},
|
},
|
||||||
Expr {
|
Expr {
|
||||||
|
@ -728,14 +746,14 @@ mod tests {
|
||||||
let expected = &[Stmt {
|
let expected = &[Stmt {
|
||||||
kind: StmtKind::Print(Expr {
|
kind: StmtKind::Print(Expr {
|
||||||
kind: ExprKind::Index {
|
kind: ExprKind::Index {
|
||||||
cart: Box::new(Expr {
|
expr: Box::new(Expr {
|
||||||
kind: ExprKind::Cart(vec![(
|
kind: ExprKind::Cart(vec![(
|
||||||
Expr {
|
Expr {
|
||||||
kind: ExprKind::Literal(Value::Str("able".to_string())),
|
kind: ExprKind::Literal(Value::Str("able".to_owned())),
|
||||||
span: 1..7,
|
span: 1..7,
|
||||||
},
|
},
|
||||||
Expr {
|
Expr {
|
||||||
kind: ExprKind::Literal(Value::Str("ablecorp".to_string())),
|
kind: ExprKind::Literal(Value::Str("ablecorp".to_owned())),
|
||||||
span: 11..21,
|
span: 11..21,
|
||||||
},
|
},
|
||||||
)]),
|
)]),
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell, collections::HashMap, convert::TryFrom, fmt::Display, hash::Hash, io::Write,
|
cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, io::Write, mem::discriminant,
|
||||||
mem::discriminant, rc::Rc,
|
ops, rc::Rc, vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::{ast::Stmt, consts};
|
use crate::{ast::Stmt, brian::INSTRUCTION_MAPPINGS, consts};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
pub enum Abool {
|
pub enum Abool {
|
||||||
Never = -1,
|
Never = -1,
|
||||||
Sometimes = 0,
|
Sometimes = 0,
|
||||||
|
@ -36,16 +36,43 @@ impl From<Abool> for bool {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||||
pub enum Functio {
|
pub enum Functio {
|
||||||
BfFunctio {
|
Bf {
|
||||||
instructions: Vec<u8>,
|
instructions: Vec<u8>,
|
||||||
tape_len: usize,
|
tape_len: usize,
|
||||||
},
|
},
|
||||||
AbleFunctio {
|
Able {
|
||||||
params: Vec<String>,
|
params: Vec<String>,
|
||||||
body: Vec<Stmt>,
|
body: Vec<Stmt>,
|
||||||
},
|
},
|
||||||
|
Chain {
|
||||||
|
functios: Box<(Functio, Functio)>,
|
||||||
|
kind: FunctioChainKind,
|
||||||
|
},
|
||||||
|
Eval(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Functio {
|
||||||
|
pub fn arity(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions: _,
|
||||||
|
tape_len: _,
|
||||||
|
} => 0,
|
||||||
|
Functio::Able { params, body: _ } => params.len(),
|
||||||
|
Functio::Chain { functios, kind: _ } => functios.0.arity() + functios.1.arity(),
|
||||||
|
Functio::Eval(_) => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone, Hash)]
|
||||||
|
pub enum FunctioChainKind {
|
||||||
|
Equal,
|
||||||
|
ByArity,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Cart = HashMap<Value, Rc<RefCell<Value>>>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Nul,
|
Nul,
|
||||||
|
@ -54,7 +81,13 @@ pub enum Value {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Abool(Abool),
|
Abool(Abool),
|
||||||
Functio(Functio),
|
Functio(Functio),
|
||||||
Cart(HashMap<Value, Rc<RefCell<Value>>>),
|
Cart(Cart),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Value {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Nul
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Value {
|
impl Hash for Value {
|
||||||
|
@ -72,24 +105,6 @@ impl Hash for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Value {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Nul, Value::Nul) => true,
|
|
||||||
(Value::Str(left), Value::Str(right)) => left == right,
|
|
||||||
(Value::Int(left), Value::Int(right)) => left == right,
|
|
||||||
(Value::Bool(left), Value::Bool(right)) => left == right,
|
|
||||||
(Value::Abool(left), Value::Abool(right)) => left == right,
|
|
||||||
(Value::Functio(left), Value::Functio(right)) => left == right,
|
|
||||||
(Value::Cart(_), Value::Cart(_)) => self.to_string() == other.to_string(),
|
|
||||||
(_, _) => false,
|
|
||||||
// TODO: do more coercions!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Value {}
|
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
/// Write an AbleScript value to a Brainfuck input stream by
|
/// Write an AbleScript value to a Brainfuck input stream by
|
||||||
/// coercing the value to an integer, then truncating that integer
|
/// coercing the value to an integer, then truncating that integer
|
||||||
|
@ -98,73 +113,668 @@ impl Value {
|
||||||
/// any IO errors will cause a panic.
|
/// any IO errors will cause a panic.
|
||||||
pub fn bf_write(&self, stream: &mut impl Write) {
|
pub fn bf_write(&self, stream: &mut impl Write) {
|
||||||
stream
|
stream
|
||||||
.write_all(&[self.clone().to_i32() as u8])
|
.write_all(&[self.clone().into_i32() as u8])
|
||||||
.expect("Failed to write to Brainfuck input");
|
.expect("Failed to write to Brainfuck input");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce a value to an integer.
|
/// Coerce a value to an integer.
|
||||||
pub fn to_i32(&self) -> i32 {
|
pub fn into_i32(self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
Value::Abool(a) => *a as _,
|
Value::Abool(a) => a as _,
|
||||||
Value::Bool(b) => *b as _,
|
Value::Bool(b) => b as _,
|
||||||
Value::Functio(func) => match func {
|
Value::Functio(func) => match func {
|
||||||
Functio::BfFunctio {
|
// Compares lengths of functions:
|
||||||
|
// BfFunctio - Sum of lengths of instructions and length of tape
|
||||||
|
// AbleFunctio - Sum of argument count and body length
|
||||||
|
// Eval - Length of input code
|
||||||
|
Functio::Bf {
|
||||||
instructions,
|
instructions,
|
||||||
tape_len,
|
tape_len,
|
||||||
} => (instructions.len() + tape_len) as _,
|
} => (instructions.len() + tape_len) as _,
|
||||||
Functio::AbleFunctio { params, body } => (params.len() + body.len()) as _,
|
Functio::Able { params, body } => (params.len() + format!("{:?}", body).len()) as _,
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (lhs, rhs) = *functios;
|
||||||
|
match kind {
|
||||||
|
FunctioChainKind::Equal => {
|
||||||
|
Value::Int(Value::Functio(lhs).into_i32())
|
||||||
|
+ Value::Int(Value::Functio(rhs).into_i32())
|
||||||
|
}
|
||||||
|
FunctioChainKind::ByArity => {
|
||||||
|
Value::Int(Value::Functio(lhs).into_i32())
|
||||||
|
* Value::Int(Value::Functio(rhs).into_i32())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into_i32()
|
||||||
|
}
|
||||||
|
Functio::Eval(s) => s.len() as _,
|
||||||
},
|
},
|
||||||
Value::Int(i) => *i,
|
Value::Int(i) => i,
|
||||||
Value::Nul => consts::ANSWER,
|
Value::Nul => consts::ANSWER,
|
||||||
Value::Str(text) => text.parse().unwrap_or(consts::ANSWER),
|
Value::Str(text) => text.parse().unwrap_or(consts::ANSWER),
|
||||||
Value::Cart(c) => c.len() as _,
|
Value::Cart(c) => c.len() as _,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce a Value to a boolean. The conversion cannot fail.
|
/// Coerce a value to a boolean.
|
||||||
pub fn to_bool(&self) -> bool {
|
pub fn into_bool(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Value::Abool(b) => (*b).into(),
|
Value::Abool(b) => b.into(),
|
||||||
Value::Bool(b) => *b,
|
Value::Bool(b) => b,
|
||||||
Value::Functio(_) => true,
|
Value::Functio(_) => true,
|
||||||
Value::Int(x) => *x != 0,
|
Value::Int(x) => x != 0,
|
||||||
Value::Nul => true,
|
Value::Nul => false,
|
||||||
Value::Str(s) => !s.is_empty(),
|
Value::Str(s) => match s.to_lowercase().as_str() {
|
||||||
|
"false" | "no" | "🇳🇴" => false,
|
||||||
|
"true" | "yes" => true,
|
||||||
|
s => !s.is_empty(),
|
||||||
|
},
|
||||||
Value::Cart(c) => !c.is_empty(),
|
Value::Cart(c) => !c.is_empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Index a value with another value, as in the "a[b]" syntax.
|
/// Coerce a value to an aboolean.
|
||||||
pub fn index(&self, index: &Value) -> Rc<RefCell<Value>> {
|
pub fn into_abool(self) -> Abool {
|
||||||
Rc::new(RefCell::new(match self {
|
match self {
|
||||||
Value::Nul => Value::Nul,
|
Value::Nul => Abool::Never,
|
||||||
Value::Str(s) => Value::Int(
|
Value::Str(s) => match s.to_lowercase().as_str() {
|
||||||
usize::try_from(index.to_i32() - 1)
|
"never" => Abool::Never,
|
||||||
.ok()
|
"sometimes" => Abool::Sometimes,
|
||||||
.and_then(|idx| s.as_bytes().get(idx).cloned())
|
"always" => Abool::Always,
|
||||||
.map(|value| value as i32)
|
s => {
|
||||||
.unwrap_or(0),
|
if s.is_empty() {
|
||||||
),
|
Abool::Never
|
||||||
Value::Int(i) => Value::Int(
|
} else {
|
||||||
usize::try_from(index.to_i32() - 1)
|
Abool::Always
|
||||||
.ok()
|
}
|
||||||
.and_then(|idx| format!("{}", i).as_bytes().get(idx).cloned())
|
}
|
||||||
.map(|value| value as i32)
|
},
|
||||||
.unwrap_or(0),
|
Value::Int(x) => match x.cmp(&0) {
|
||||||
),
|
std::cmp::Ordering::Less => Abool::Never,
|
||||||
Value::Bool(b) => Value::Int(
|
std::cmp::Ordering::Equal => Abool::Sometimes,
|
||||||
usize::try_from(index.to_i32() - 1)
|
std::cmp::Ordering::Greater => Abool::Always,
|
||||||
.ok()
|
},
|
||||||
.and_then(|idx| format!("{}", b).as_bytes().get(idx).cloned())
|
Value::Bool(b) => {
|
||||||
.map(|value| value as i32)
|
if b {
|
||||||
.unwrap_or(0),
|
Abool::Always
|
||||||
),
|
} else {
|
||||||
Value::Abool(b) => Value::Int(*b as i32),
|
Abool::Never
|
||||||
Value::Functio(_) => Value::Int(42),
|
}
|
||||||
Value::Cart(c) => {
|
|
||||||
return (c.get(index).cloned()).unwrap_or_else(|| Rc::new(RefCell::new(Value::Nul)))
|
|
||||||
}
|
}
|
||||||
}))
|
Value::Abool(a) => a,
|
||||||
|
Value::Functio(f) => match f {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => Value::Int(
|
||||||
|
(instructions.iter().map(|x| *x as usize).sum::<usize>() * tape_len) as _,
|
||||||
|
)
|
||||||
|
.into_abool(),
|
||||||
|
Functio::Able { params, body } => {
|
||||||
|
let str_to_i32 =
|
||||||
|
|x: String| -> i32 { x.as_bytes().into_iter().map(|x| *x as i32).sum() };
|
||||||
|
|
||||||
|
let params: i32 = params.into_iter().map(str_to_i32).sum();
|
||||||
|
let body: i32 = body
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| format!("{:?}", x))
|
||||||
|
.map(str_to_i32)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
Value::Int((params + body) % 3 - 1).into_abool()
|
||||||
|
}
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (lhs, rhs) = *functios;
|
||||||
|
match kind {
|
||||||
|
FunctioChainKind::Equal => {
|
||||||
|
Value::Abool(Value::Functio(lhs).into_abool())
|
||||||
|
+ Value::Abool(Value::Functio(rhs).into_abool())
|
||||||
|
}
|
||||||
|
FunctioChainKind::ByArity => {
|
||||||
|
Value::Abool(Value::Functio(lhs).into_abool())
|
||||||
|
* Value::Abool(Value::Functio(rhs).into_abool())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into_abool()
|
||||||
|
}
|
||||||
|
Functio::Eval(code) => Value::Str(code).into_abool(),
|
||||||
|
},
|
||||||
|
Value::Cart(c) => {
|
||||||
|
if c.is_empty() {
|
||||||
|
Abool::Never
|
||||||
|
} else {
|
||||||
|
Abool::Always
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Coerce a value to a functio.
|
||||||
|
pub fn into_functio(self) -> Functio {
|
||||||
|
match self {
|
||||||
|
Value::Nul => Functio::Able {
|
||||||
|
body: vec![],
|
||||||
|
params: vec![],
|
||||||
|
},
|
||||||
|
Value::Str(s) => Functio::Eval(s),
|
||||||
|
Value::Int(i) => Functio::Bf {
|
||||||
|
instructions: {
|
||||||
|
std::iter::successors(Some(i as usize), |i| {
|
||||||
|
Some(i / INSTRUCTION_MAPPINGS.len())
|
||||||
|
})
|
||||||
|
.take_while(|&i| i != 0)
|
||||||
|
.map(|i| INSTRUCTION_MAPPINGS[i % INSTRUCTION_MAPPINGS.len()])
|
||||||
|
.collect()
|
||||||
|
},
|
||||||
|
tape_len: crate::brian::DEFAULT_TAPE_SIZE_LIMIT,
|
||||||
|
},
|
||||||
|
Value::Bool(b) => Functio::Eval(
|
||||||
|
if b {
|
||||||
|
r#"loop{"Buy Able products!"print;}"#
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
.to_owned(),
|
||||||
|
),
|
||||||
|
Value::Abool(a) => Functio::Eval(match a {
|
||||||
|
Abool::Never => "".to_owned(),
|
||||||
|
Abool::Sometimes => {
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
let mut str_chars: Vec<_> = "Buy Able Products!".chars().collect();
|
||||||
|
str_chars.shuffle(&mut rand::thread_rng());
|
||||||
|
|
||||||
|
format!(r#""{}"print;"#, str_chars.iter().collect::<String>())
|
||||||
|
}
|
||||||
|
Abool::Always => r#"loop{"Buy Able products!"print;}"#.to_owned(),
|
||||||
|
}),
|
||||||
|
Value::Functio(f) => f,
|
||||||
|
Value::Cart(c) => {
|
||||||
|
let kind = if let Some(114514) = c
|
||||||
|
.get(&Value::Str("1452251871514141792252515212116".to_owned()))
|
||||||
|
.map(|x| x.borrow().to_owned().into_i32())
|
||||||
|
{
|
||||||
|
FunctioChainKind::Equal
|
||||||
|
} else {
|
||||||
|
FunctioChainKind::ByArity
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cart_vec = c.iter().collect::<Vec<_>>();
|
||||||
|
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
|
||||||
|
|
||||||
|
cart_vec
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, x)| x.borrow().to_owned().into_functio())
|
||||||
|
.reduce(|acc, x| Functio::Chain {
|
||||||
|
functios: Box::new((acc, x)),
|
||||||
|
kind,
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| Functio::Eval(r#""Buy Able Products!"print;"#.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Coerce a value into a cart.
|
||||||
|
pub fn into_cart(self) -> Cart {
|
||||||
|
match self {
|
||||||
|
Value::Nul => HashMap::new(),
|
||||||
|
Value::Str(s) => s
|
||||||
|
.chars()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as i32 + 1),
|
||||||
|
Rc::new(RefCell::new(Value::Str(x.to_string()))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
Value::Int(i) => Value::Str(i.to_string()).into_cart(),
|
||||||
|
Value::Bool(b) => Value::Str(b.to_string()).into_cart(),
|
||||||
|
Value::Abool(a) => Value::Str(a.to_string()).into_cart(),
|
||||||
|
Value::Functio(f) => match f {
|
||||||
|
Functio::Able { params, body } => {
|
||||||
|
let params: Cart = params
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as i32 + 1),
|
||||||
|
Rc::new(RefCell::new(Value::Str(x))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let body: Cart = body
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as i32 + 1),
|
||||||
|
Rc::new(RefCell::new(Value::Str(format!("{:?}", x)))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut cart = HashMap::new();
|
||||||
|
cart.insert(
|
||||||
|
Value::Str("params".to_owned()),
|
||||||
|
Rc::new(RefCell::new(Value::Cart(params))),
|
||||||
|
);
|
||||||
|
|
||||||
|
cart.insert(
|
||||||
|
Value::Str("body".to_owned()),
|
||||||
|
Rc::new(RefCell::new(Value::Cart(body))),
|
||||||
|
);
|
||||||
|
|
||||||
|
cart
|
||||||
|
}
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => {
|
||||||
|
let mut cart: Cart = instructions
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as i32 + 1),
|
||||||
|
Rc::new(RefCell::new(
|
||||||
|
char::from_u32(x as u32)
|
||||||
|
.map(|x| Value::Str(x.to_string()))
|
||||||
|
.unwrap_or(Value::Nul),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
cart.insert(
|
||||||
|
Value::Str("tapelen".to_owned()),
|
||||||
|
Rc::new(RefCell::new(Value::Int(tape_len as _))),
|
||||||
|
);
|
||||||
|
cart
|
||||||
|
}
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (lhs, rhs) = *functios;
|
||||||
|
match kind {
|
||||||
|
FunctioChainKind::Equal => {
|
||||||
|
Value::Cart(Value::Functio(lhs).into_cart())
|
||||||
|
+ Value::Cart(Value::Functio(rhs).into_cart())
|
||||||
|
}
|
||||||
|
FunctioChainKind::ByArity => {
|
||||||
|
Value::Cart(Value::Functio(lhs).into_cart())
|
||||||
|
* Value::Cart(Value::Functio(rhs).into_cart())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into_cart()
|
||||||
|
}
|
||||||
|
Functio::Eval(s) => Value::Str(s).into_cart(),
|
||||||
|
},
|
||||||
|
Value::Cart(c) => c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul => match rhs {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Str(_) => Value::Str(self.to_string()) + rhs,
|
||||||
|
Value::Int(_) => Value::Int(self.into_i32()) + rhs,
|
||||||
|
Value::Bool(_) => Value::Bool(self.into_bool()) + rhs,
|
||||||
|
Value::Abool(_) => Value::Abool(self.into_abool()) + rhs,
|
||||||
|
Value::Functio(_) => Value::Functio(self.into_functio()) + rhs,
|
||||||
|
Value::Cart(_) => Value::Cart(self.into_cart()) + rhs,
|
||||||
|
},
|
||||||
|
Value::Str(s) => Value::Str(format!("{}{}", s, rhs.to_string())),
|
||||||
|
Value::Int(i) => Value::Int(i.wrapping_add(rhs.into_i32())),
|
||||||
|
Value::Bool(b) => Value::Bool(b || rhs.into_bool()),
|
||||||
|
Value::Abool(_) => {
|
||||||
|
Value::Abool(Value::Int(self.into_i32().max(rhs.into_i32())).into_abool())
|
||||||
|
}
|
||||||
|
Value::Functio(f) => Value::Functio(Functio::Chain {
|
||||||
|
functios: Box::new((f, rhs.into_functio())),
|
||||||
|
kind: FunctioChainKind::Equal,
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => {
|
||||||
|
Value::Cart(c.into_iter().chain(rhs.into_cart().into_iter()).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul => match rhs {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Str(_) => Value::Str(self.to_string()) - rhs,
|
||||||
|
Value::Int(_) => Value::Int(self.into_i32()) - rhs,
|
||||||
|
Value::Bool(_) => Value::Bool(self.into_bool()) - rhs,
|
||||||
|
Value::Abool(_) => Value::Abool(self.into_abool()) - rhs,
|
||||||
|
Value::Functio(_) => Value::Functio(self.into_functio()) - rhs,
|
||||||
|
Value::Cart(_) => Value::Cart(self.into_cart()) - rhs,
|
||||||
|
},
|
||||||
|
Value::Str(s) => Value::Str(s.replace(&rhs.to_string(), "")),
|
||||||
|
Value::Int(i) => Value::Int(i.wrapping_sub(rhs.into_i32())),
|
||||||
|
Value::Bool(b) => Value::Bool(b ^ rhs.into_bool()),
|
||||||
|
Value::Abool(_) => (self.clone() + rhs.clone()) * !(self * rhs),
|
||||||
|
Value::Functio(f) => Value::Functio(match f {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions: lhs_ins,
|
||||||
|
tape_len: lhs_tl,
|
||||||
|
} => match rhs.into_functio() {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions: rhs_ins,
|
||||||
|
tape_len: rhs_tl,
|
||||||
|
} => Functio::Bf {
|
||||||
|
instructions: lhs_ins
|
||||||
|
.into_iter()
|
||||||
|
.zip(rhs_ins.into_iter())
|
||||||
|
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
||||||
|
.collect(),
|
||||||
|
tape_len: lhs_tl - rhs_tl,
|
||||||
|
},
|
||||||
|
rhs => Functio::Bf {
|
||||||
|
instructions: lhs_ins
|
||||||
|
.into_iter()
|
||||||
|
.zip(Value::Functio(rhs).to_string().bytes())
|
||||||
|
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
||||||
|
.collect(),
|
||||||
|
tape_len: lhs_tl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Functio::Able {
|
||||||
|
params: lhs_params,
|
||||||
|
body: lhs_body,
|
||||||
|
} => match rhs.into_functio() {
|
||||||
|
Functio::Able {
|
||||||
|
params: rhs_params,
|
||||||
|
body: rhs_body,
|
||||||
|
} => Functio::Able {
|
||||||
|
params: lhs_params
|
||||||
|
.into_iter()
|
||||||
|
.zip(rhs_params.into_iter())
|
||||||
|
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
||||||
|
.collect(),
|
||||||
|
body: lhs_body
|
||||||
|
.into_iter()
|
||||||
|
.zip(rhs_body.into_iter())
|
||||||
|
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
rhs => Value::Int(
|
||||||
|
Value::Functio(Functio::Able {
|
||||||
|
params: lhs_params,
|
||||||
|
body: lhs_body,
|
||||||
|
})
|
||||||
|
.into_i32()
|
||||||
|
- Value::Functio(rhs).into_i32(),
|
||||||
|
)
|
||||||
|
.into_functio(),
|
||||||
|
},
|
||||||
|
Functio::Chain { functios, .. } => {
|
||||||
|
let rhs = rhs.into_functio();
|
||||||
|
let (a, b) = *functios;
|
||||||
|
|
||||||
|
match (a == rhs, b == rhs) {
|
||||||
|
(_, true) => a,
|
||||||
|
(true, _) => b,
|
||||||
|
(_, _) => (Value::Functio(a) - Value::Functio(rhs)).into_functio(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Eval(lhs_code) => Functio::Eval(lhs_code.replace(
|
||||||
|
&match rhs.into_functio() {
|
||||||
|
Functio::Eval(code) => code,
|
||||||
|
rhs => Value::Functio(rhs).to_string(),
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => Value::Cart({
|
||||||
|
let rhs_cart = rhs.into_cart();
|
||||||
|
c.into_iter()
|
||||||
|
.filter(|(k, v)| rhs_cart.get(k) != Some(v))
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul => match rhs {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Str(_) => Value::Str(self.to_string()) * rhs,
|
||||||
|
Value::Int(_) => Value::Int(self.into_i32()) * rhs,
|
||||||
|
Value::Bool(_) => Value::Bool(self.into_bool()) * rhs,
|
||||||
|
Value::Abool(_) => Value::Abool(self.into_abool()) * rhs,
|
||||||
|
Value::Functio(_) => Value::Functio(self.into_functio()) * rhs,
|
||||||
|
Value::Cart(_) => Value::Cart(self.into_cart()) * rhs,
|
||||||
|
},
|
||||||
|
Value::Str(s) => Value::Str(s.repeat(rhs.into_i32() as usize)),
|
||||||
|
Value::Int(i) => Value::Int(i.wrapping_mul(rhs.into_i32())),
|
||||||
|
Value::Bool(b) => Value::Bool(b && rhs.into_bool()),
|
||||||
|
Value::Abool(_) => {
|
||||||
|
Value::Abool(Value::Int(self.into_i32().min(rhs.into_i32())).into_abool())
|
||||||
|
}
|
||||||
|
Value::Functio(f) => Value::Functio(Functio::Chain {
|
||||||
|
functios: Box::new((f, rhs.into_functio())),
|
||||||
|
kind: FunctioChainKind::ByArity,
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => {
|
||||||
|
let rhsc = rhs.into_cart();
|
||||||
|
|
||||||
|
Value::Cart(
|
||||||
|
c.into_iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
if let Some(k) = rhsc.get(&k) {
|
||||||
|
(k.borrow().clone(), v)
|
||||||
|
} else {
|
||||||
|
(k, v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Div for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul => match rhs {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Str(_) => Value::Str(self.to_string()) / rhs,
|
||||||
|
Value::Int(_) => Value::Int(self.into_i32()) / rhs,
|
||||||
|
Value::Bool(_) => Value::Bool(self.into_bool()) / rhs,
|
||||||
|
Value::Abool(_) => Value::Abool(self.into_abool()) / rhs,
|
||||||
|
Value::Functio(_) => Value::Functio(self.into_functio()) / rhs,
|
||||||
|
Value::Cart(_) => Value::Cart(self.into_cart()) / rhs,
|
||||||
|
},
|
||||||
|
Value::Str(s) => Value::Cart(
|
||||||
|
s.split(&rhs.to_string())
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| {
|
||||||
|
(
|
||||||
|
Value::Int(i as i32 + 1),
|
||||||
|
Rc::new(RefCell::new(Value::Str(x.to_owned()))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
Value::Int(i) => Value::Int(i.wrapping_div(match rhs.into_i32() {
|
||||||
|
0 => consts::ANSWER,
|
||||||
|
x => x,
|
||||||
|
})),
|
||||||
|
Value::Bool(b) => Value::Bool(!b || rhs.into_bool()),
|
||||||
|
Value::Abool(_) => !self + rhs,
|
||||||
|
Value::Functio(f) => Value::Functio(match f {
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
} => {
|
||||||
|
let fraction = 1.0 / rhs.into_i32() as f64;
|
||||||
|
let len = instructions.len();
|
||||||
|
Functio::Bf {
|
||||||
|
instructions: instructions
|
||||||
|
.into_iter()
|
||||||
|
.take((len as f64 * fraction) as usize)
|
||||||
|
.collect(),
|
||||||
|
tape_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Able { params, body } => {
|
||||||
|
let fraction = 1.0 / rhs.into_i32() as f64;
|
||||||
|
let len = body.len();
|
||||||
|
Functio::Able {
|
||||||
|
params,
|
||||||
|
body: body
|
||||||
|
.into_iter()
|
||||||
|
.take((len as f64 * fraction) as usize)
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let functios = *functios;
|
||||||
|
Functio::Chain {
|
||||||
|
functios: Box::new((
|
||||||
|
(Value::Functio(functios.0) / rhs.clone()).into_functio(),
|
||||||
|
(Value::Functio(functios.1) / rhs).into_functio(),
|
||||||
|
)),
|
||||||
|
kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Eval(s) => {
|
||||||
|
let fraction = 1.0 / rhs.into_i32() as f64;
|
||||||
|
let len = s.len();
|
||||||
|
Functio::Eval(s.chars().take((len as f64 * fraction) as usize).collect())
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => {
|
||||||
|
let cart_len = c.len();
|
||||||
|
let chunk_len = rhs.into_i32() as usize;
|
||||||
|
|
||||||
|
Value::Cart(
|
||||||
|
c.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.chunks(cart_len / chunk_len + (cart_len % chunk_len != 0) as usize)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
(
|
||||||
|
Value::Int(k as i32 + 1),
|
||||||
|
Rc::new(RefCell::new(Value::Cart(v.iter().cloned().collect()))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Not for Value {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Value::Nul => Value::Nul,
|
||||||
|
Value::Str(s) => Value::Str(s.chars().rev().collect()),
|
||||||
|
Value::Int(i) => Value::Int(i.swap_bytes()),
|
||||||
|
Value::Bool(b) => Value::Bool(!b),
|
||||||
|
Value::Abool(a) => Value::Abool(match a {
|
||||||
|
Abool::Never => Abool::Always,
|
||||||
|
Abool::Sometimes => Abool::Sometimes,
|
||||||
|
Abool::Always => Abool::Never,
|
||||||
|
}),
|
||||||
|
Value::Functio(f) => Value::Functio(match f {
|
||||||
|
Functio::Bf {
|
||||||
|
mut instructions,
|
||||||
|
tape_len,
|
||||||
|
} => {
|
||||||
|
instructions.reverse();
|
||||||
|
|
||||||
|
Functio::Bf {
|
||||||
|
instructions,
|
||||||
|
tape_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Able {
|
||||||
|
mut params,
|
||||||
|
mut body,
|
||||||
|
} => {
|
||||||
|
params.reverse();
|
||||||
|
body.reverse();
|
||||||
|
|
||||||
|
Functio::Able { params, body }
|
||||||
|
}
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (a, b) = *functios;
|
||||||
|
Functio::Chain {
|
||||||
|
functios: Box::new((
|
||||||
|
(!Value::Functio(b)).into_functio(),
|
||||||
|
(!Value::Functio(a)).into_functio(),
|
||||||
|
)),
|
||||||
|
kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Functio::Eval(code) => Functio::Eval(code.chars().rev().collect()),
|
||||||
|
}),
|
||||||
|
Value::Cart(c) => Value::Cart(
|
||||||
|
c.into_iter()
|
||||||
|
.map(|(k, v)| (v.borrow().clone(), Rc::new(RefCell::new(k))))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Value {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
let other = other.clone();
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Value::Nul => matches!(other, Value::Nul),
|
||||||
|
Value::Str(s) => *s == other.to_string(),
|
||||||
|
Value::Int(i) => *i == other.into_i32(),
|
||||||
|
Value::Bool(b) => *b == other.into_bool(),
|
||||||
|
Value::Abool(a) => *a == other.into_abool(),
|
||||||
|
Value::Functio(f) => *f == other.into_functio(),
|
||||||
|
Value::Cart(c) => *c == other.into_cart(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Value {}
|
||||||
|
|
||||||
|
impl PartialOrd for Value {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
use std::cmp::Ordering::*;
|
||||||
|
let other = other.clone();
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Value::Nul => {
|
||||||
|
if other == Value::Nul {
|
||||||
|
Some(Equal)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Str(s) => Some(s.cmp(&other.to_string())),
|
||||||
|
Value::Int(i) => Some(i.cmp(&other.into_i32())),
|
||||||
|
Value::Bool(b) => Some(b.cmp(&other.into_bool())),
|
||||||
|
Value::Abool(a) => a.partial_cmp(&other.into_abool()),
|
||||||
|
Value::Functio(_) => self.clone().into_i32().partial_cmp(&other.into_i32()),
|
||||||
|
Value::Cart(c) => Some(c.len().cmp(&other.into_cart().len())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +787,7 @@ impl Display for Value {
|
||||||
Value::Bool(v) => write!(f, "{}", v),
|
Value::Bool(v) => write!(f, "{}", v),
|
||||||
Value::Abool(v) => write!(f, "{}", v),
|
Value::Abool(v) => write!(f, "{}", v),
|
||||||
Value::Functio(v) => match v {
|
Value::Functio(v) => match v {
|
||||||
Functio::BfFunctio {
|
Functio::Bf {
|
||||||
instructions,
|
instructions,
|
||||||
tape_len,
|
tape_len,
|
||||||
} => {
|
} => {
|
||||||
|
@ -189,7 +799,7 @@ impl Display for Value {
|
||||||
.expect("Brainfuck functio source should be UTF-8")
|
.expect("Brainfuck functio source should be UTF-8")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Functio::AbleFunctio { params, body } => {
|
Functio::Able { params, body } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"({}) -> {:?}",
|
"({}) -> {:?}",
|
||||||
|
@ -199,12 +809,34 @@ impl Display for Value {
|
||||||
body,
|
body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Functio::Chain { functios, kind } => {
|
||||||
|
let (a, b) = *functios.clone();
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {} {} ",
|
||||||
|
Value::Functio(a),
|
||||||
|
match kind {
|
||||||
|
FunctioChainKind::Equal => '+',
|
||||||
|
FunctioChainKind::ByArity => '*',
|
||||||
|
},
|
||||||
|
Value::Functio(b)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Functio::Eval(s) => write!(f, "{}", s),
|
||||||
},
|
},
|
||||||
Value::Cart(c) => {
|
Value::Cart(c) => {
|
||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
|
let mut cart_vec = c.iter().collect::<Vec<_>>();
|
||||||
|
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
|
||||||
|
|
||||||
for (key, value) in c {
|
for (idx, (key, value)) in cart_vec.into_iter().enumerate() {
|
||||||
write!(f, "{} <= {},", value.borrow(), key)?;
|
write!(
|
||||||
|
f,
|
||||||
|
"{}{} <= {}",
|
||||||
|
if idx != 0 { ", " } else { "" },
|
||||||
|
value.borrow(),
|
||||||
|
key
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "]")
|
write!(f, "]")
|
||||||
|
|
Loading…
Reference in a new issue