diff --git a/able-script-test/by-arity-chain.able b/able-script-test/by-arity-chain.able new file mode 100644 index 0000000..2dca191 --- /dev/null +++ b/able-script-test/by-arity-chain.able @@ -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"); diff --git a/able-script-test/carts.able b/able-script-test/carts.able index 21b5a0d..b12ef8d 100644 --- a/able-script-test/carts.able +++ b/able-script-test/carts.able @@ -1,8 +1,8 @@ functio helloable() { - "Hello, Able!" print; + "Hello, Able!" print; } var cart = ["able" <= 42, helloable <= "hello"]; cart[42] print; -cart["hello"](); \ No newline at end of file +cart["hello"](); diff --git a/ablescript/src/ast.rs b/ablescript/src/ast.rs index 9043283..a5f1905 100644 --- a/ablescript/src/ast.rs +++ b/ablescript/src/ast.rs @@ -15,26 +15,71 @@ use crate::variables::Value; type Span = std::ops::Range; #[derive(Debug, Clone)] -pub struct Iden { - pub iden: String, +pub struct Ident { + pub ident: String, pub span: Span, } -impl Iden { - pub fn new(iden: String, span: Span) -> Self { - Self { iden, span } +impl Ident { + pub fn new(ident: String, span: Span) -> Self { + Self { ident, span } } } -impl PartialEq for Iden { +impl PartialEq for Ident { fn eq(&self, other: &Self) -> bool { - self.iden == other.iden + self.ident == other.ident } } -impl Hash for Iden { +impl Hash for Ident { fn hash(&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 }, +} + +impl Assignable { + pub fn from_expr(expr: Expr) -> Result { + 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 { + 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, Var { - iden: Iden, + ident: Ident, init: Option, }, Assign { - iden: Iden, + assignable: Assignable, value: Expr, }, Functio { - iden: Iden, - params: Vec, + ident: Ident, + params: Vec, body: Block, }, BfFunctio { - iden: Iden, + ident: Ident, tape_len: Option, code: Vec, }, @@ -99,8 +144,8 @@ pub enum StmtKind { args: Vec, }, Print(Expr), - Read(Iden), - Melo(Iden), + Read(Assignable), + Melo(Ident), Rlyeh, Rickroll, } @@ -142,7 +187,7 @@ pub enum ExprKind { Literal(Value), Cart(Vec<(Expr, Expr)>), Index { - cart: Box, + expr: Box, index: Box, }, Variable(String), @@ -164,8 +209,6 @@ pub enum BinOpKind { Less, Equal, NotEqual, - And, - Or, } impl BinOpKind { @@ -181,8 +224,6 @@ impl BinOpKind { Token::LessThan => Ok(Self::Less), Token::EqualEqual => Ok(Self::Equal), Token::NotEqual => Ok(Self::NotEqual), - Token::And => Ok(Self::And), - Token::Or => Ok(Self::Or), t => Err(crate::error::ErrorKind::UnexpectedToken(t)), } } diff --git a/ablescript/src/base_55.rs b/ablescript/src/base_55.rs index fa612eb..a822227 100644 --- a/ablescript/src/base_55.rs +++ b/ablescript/src/base_55.rs @@ -60,68 +60,6 @@ pub fn char2num(character: char) -> i32 { _ => 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)] mod tests { diff --git a/ablescript/src/brian.rs b/ablescript/src/brian.rs index 16cb25e..a915156 100644 --- a/ablescript/src/brian.rs +++ b/ablescript/src/brian.rs @@ -17,6 +17,8 @@ //! [`interpret_with_output`]: Interpreter::interpret_with_output #![deny(missing_docs)] +// Putting this here because we still don't use the entire capabilities of this module. ~~Alex +#![allow(dead_code)] use std::{ 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 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)] /// A brainfuck interpreter. Read the [module level documentation](self) for more pub struct Interpreter<'a, I> { diff --git a/ablescript/src/interpret.rs b/ablescript/src/interpret.rs index 1f0f0f6..183eb9f 100644 --- a/ablescript/src/interpret.rs +++ b/ablescript/src/interpret.rs @@ -9,8 +9,10 @@ #![deny(missing_docs)] use std::{ cell::RefCell, + cmp::Ordering, collections::{HashMap, VecDeque}, io::{stdin, stdout, Read, Write}, + mem::take, ops::Range, process::exit, rc::Rc, @@ -19,9 +21,9 @@ use std::{ use rand::random; use crate::{ - ast::{Expr, ExprKind, Iden, Stmt, StmtKind}, + ast::{Assignable, AssignableKind, Expr, ExprKind, Ident, Stmt, StmtKind}, base_55, - consts::{self, ablescript_consts}, + consts::ablescript_consts, error::{Error, ErrorKind}, variables::{Functio, Value, Variable}, }; @@ -134,66 +136,23 @@ impl ExecEnv { fn eval_expr(&self, expr: &Expr) -> Result { use crate::ast::BinOpKind::*; use crate::ast::ExprKind::*; - use Value::*; Ok(match &expr.kind { BinOp { lhs, rhs, kind } => { let lhs = self.eval_expr(lhs)?; let rhs = self.eval_expr(rhs)?; match kind { - // Arithmetic operators. - Add | Subtract | Multiply | Divide => { - let lhs = lhs.to_i32(); - let rhs = rhs.to_i32(); - - let res = match kind { - Add => lhs.checked_add(rhs), - Subtract => lhs.checked_sub(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) - } + Add => lhs + rhs, + Subtract => lhs - rhs, + Multiply => lhs * rhs, + Divide => lhs / rhs, + Greater => Value::Bool(lhs > rhs), + Less => Value::Bool(lhs < rhs), + Equal => Value::Bool(lhs == rhs), + NotEqual => Value::Bool(lhs != rhs), } } - Not(expr) => Bool(!self.eval_expr(expr)?.to_bool()), + Not(expr) => !self.eval_expr(expr)?, Literal(value) => value.clone(), ExprKind::Cart(members) => Value::Cart( members @@ -206,18 +165,21 @@ impl ExecEnv { }) .collect::, _>>()?, ), - Index { cart, index } => { - let cart = self.eval_expr(cart)?; + Index { expr, index } => { + let value = self.eval_expr(expr)?; let index = self.eval_expr(index)?; - // TODO: this probably shouldn't be cloned - cart.index(&index).borrow().clone() + value + .into_cart() + .get(&index) + .map(|x| x.borrow().clone()) + .unwrap_or(Value::Nul) } // TODO: not too happy with constructing an artificial - // Iden here. - Variable(name) => self.get_var(&Iden { - iden: name.to_owned(), + // Ident here. + Variable(name) => self.get_var(&Ident { + ident: name.to_owned(), span: expr.span.clone(), })?, }) @@ -229,52 +191,52 @@ impl ExecEnv { StmtKind::Print(expr) => { println!("{}", self.eval_expr(expr)?); } - StmtKind::Var { iden, init } => { + StmtKind::Var { ident, init } => { let init = match init { Some(e) => self.eval_expr(e)?, 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( - &iden.iden, - Value::Functio(Functio::AbleFunctio { - params: params.iter().map(|iden| iden.iden.to_string()).collect(), + &ident.ident, + Value::Functio(Functio::Able { + params: params.iter().map(|ident| ident.ident.to_owned()).collect(), body: body.block.to_owned(), }), ); } StmtKind::BfFunctio { - iden, + ident, tape_len, code, } => { self.decl_var( - &iden.iden, - Value::Functio(Functio::BfFunctio { + &ident.ident, + 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.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))?, }), ); } 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); } } 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)?; - } else { - // Fail silently for now. - } + self.fn_call(func, args, &stmt.span)?; } StmtKind::Loop { body } => loop { let res = self.eval_stmts_hs(&body.block, true)?; @@ -284,9 +246,8 @@ impl ExecEnv { HaltStatus::Hopback(_) => continue, } }, - StmtKind::Assign { iden, value } => { - let value = self.eval_expr(value)?; - self.get_var_mut(iden)?.value.replace(value); + StmtKind::Assign { assignable, value } => { + self.assign(assignable, self.eval_expr(value)?)?; } StmtKind::Break => { return Ok(HaltStatus::Break(stmt.span.clone())); @@ -294,8 +255,8 @@ impl ExecEnv { StmtKind::HopBack => { return Ok(HaltStatus::Hopback(stmt.span.clone())); } - StmtKind::Melo(iden) => { - self.get_var_mut(iden)?.melo = true; + StmtKind::Melo(ident) => { + self.get_var_mut(ident)?.melo = true; } StmtKind::Rlyeh => { // Maybe print a creepy error message or something @@ -307,20 +268,69 @@ impl ExecEnv { .write_all(include_str!("rickroll").as_bytes()) .expect("Failed to write to stdout"); } - StmtKind::Read(iden) => { + StmtKind::Read(assignable) => { let mut value = 0; for _ in 0..READ_BITS { value <<= 1; 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) } + /// 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 /// parameters). If the function invocation fails for some reason, /// report the error at `span`. @@ -331,8 +341,8 @@ impl ExecEnv { .iter() .map(|arg| { if let ExprKind::Variable(name) = &arg.kind { - self.get_var_rc(&Iden { - iden: name.to_owned(), + self.get_var_rc(&Ident { + ident: name.to_owned(), span: arg.span.clone(), }) } else { @@ -341,8 +351,17 @@ impl ExecEnv { }) .collect::, Error>>()?; + self.fn_call_with_values(func, &args, span) + } + + fn fn_call_with_values( + &mut self, + func: Functio, + args: &[Rc>], + span: &Range, + ) -> Result<(), Error> { match func { - Functio::BfFunctio { + Functio::Bf { instructions, tape_len, } => { @@ -368,7 +387,7 @@ impl ExecEnv { .write_all(&output) .expect("Failed to write to stdout"); } - Functio::AbleFunctio { params, body } => { + Functio::Able { params, body } => { if params.len() != args.len() { return Err(Error { kind: ErrorKind::MismatchedArgumentError, @@ -387,10 +406,74 @@ impl ExecEnv { self.stack.pop(); 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(()) } + fn deinterlace( + args: &[Rc>], + arities: (usize, usize), + ) -> (Vec>>, Vec>>) { + 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 /// standard input if it is empty. fn get_bit(&mut self) -> Result { @@ -413,9 +496,9 @@ impl ExecEnv { /// Get the value of a variable. Throw an error if the variable is /// inaccessible or banned. - fn get_var(&self, name: &Iden) -> Result { + fn get_var(&self, name: &Ident) -> Result { // 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()) { return Ok(Value::Int(base_55::char2num(first))); } @@ -426,20 +509,20 @@ impl ExecEnv { .stack .iter() .rev() - .find_map(|scope| scope.variables.get(&name.iden)) + .find_map(|scope| scope.variables.get(&name.ident)) { Some(var) => { if !var.melo { Ok(var.value.borrow().clone()) } else { Err(Error { - kind: ErrorKind::MeloVariable(name.iden.to_owned()), + kind: ErrorKind::MeloVariable(name.ident.to_owned()), span: name.span.clone(), }) } } None => Err(Error { - kind: ErrorKind::UnknownVariable(name.iden.to_owned()), + kind: ErrorKind::UnknownVariable(name.ident.to_owned()), span: name.span.clone(), }), } @@ -447,27 +530,27 @@ impl ExecEnv { /// Get a mutable reference to a variable. Throw an error if the /// 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`, // which I feel like is a bad sign... match self .stack .iter_mut() .rev() - .find_map(|scope| scope.variables.get_mut(&name.iden)) + .find_map(|scope| scope.variables.get_mut(&name.ident)) { Some(var) => { if !var.melo { Ok(var) } else { Err(Error { - kind: ErrorKind::MeloVariable(name.iden.to_owned()), + kind: ErrorKind::MeloVariable(name.ident.to_owned()), span: name.span.clone(), }) } } None => Err(Error { - kind: ErrorKind::UnknownVariable(name.iden.to_owned()), + kind: ErrorKind::UnknownVariable(name.ident.to_owned()), span: name.span.clone(), }), } @@ -475,7 +558,7 @@ impl ExecEnv { /// Get an Rc'd pointer to the value of a variable. Throw an error /// if the variable is inaccessible or banned. - fn get_var_rc(&mut self, name: &Iden) -> Result>, Error> { + fn get_var_rc(&mut self, name: &Ident) -> Result>, Error> { Ok(self.get_var_mut(name)?.value.clone()) } @@ -571,7 +654,7 @@ mod tests { span: 1..1 }) .unwrap(), - Value::Int(42) + Value::Int(-2147483648) ); // And the same for divide by zero. @@ -579,7 +662,7 @@ mod tests { env.eval_expr(&Expr { kind: ExprKind::BinOp { lhs: Box::new(Expr { - kind: ExprKind::Literal(Value::Int(1)), + kind: ExprKind::Literal(Value::Int(84)), span: 1..1, }), rhs: Box::new(Expr { @@ -591,7 +674,7 @@ mod tests { span: 1..1 }) .unwrap(), - Value::Int(42) + Value::Int(2) ); } @@ -617,8 +700,8 @@ mod tests { // Declaring and reading from a variable. eval(&mut env, "var foo = 32; var bar = foo + 1;").unwrap(); assert_eq!( - env.get_var(&Iden { - iden: "bar".to_owned(), + env.get_var(&Ident { + ident: "bar".to_owned(), span: 1..1, }) .unwrap(), @@ -628,8 +711,8 @@ mod tests { // Assigning an existing variable. eval(&mut env, "foo = \"hi\";").unwrap(); assert_eq!( - env.get_var(&Iden { - iden: "foo".to_owned(), + env.get_var(&Ident { + ident: "foo".to_owned(), span: 1..1, }) .unwrap(), @@ -654,8 +737,8 @@ mod tests { .unwrap(); assert_eq!( - env.get_var(&Iden { - iden: "foo".to_owned(), + env.get_var(&Ident { + ident: "foo".to_owned(), span: 1..1, }) .unwrap(), diff --git a/ablescript/src/lexer.rs b/ablescript/src/lexer.rs index c622800..a6c5940 100644 --- a/ablescript/src/lexer.rs +++ b/ablescript/src/lexer.rs @@ -64,13 +64,7 @@ pub enum Token { #[token("!=")] NotEqual, - #[token("&")] - And, - - #[token("|")] - Or, - - #[token("!|aint")] // also add aint as a not keyword + #[regex("!|aint")] // also add aint as a not keyword Not, // Keywords @@ -123,7 +117,7 @@ pub enum Token { // Literals /// True, False - #[regex("true|false", get_bool)] + #[regex("true|false", get_value)] Bool(bool), /// Always, Sometimes, Never @@ -139,11 +133,11 @@ pub enum Token { String(String), /// Integer - #[regex(r"-?[0-9]+", get_int)] + #[regex(r"-?[0-9]+", get_value)] Integer(i32), /// 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), #[regex(r"owo .*")] @@ -157,11 +151,7 @@ pub enum Token { Error, } -fn get_bool(lexer: &mut Lexer) -> Option { - lexer.slice().parse().ok() -} - -fn get_int(lexer: &mut Lexer) -> Option { +fn get_value(lexer: &mut Lexer) -> Option { lexer.slice().parse().ok() } @@ -178,7 +168,7 @@ fn get_abool(lexer: &mut Lexer) -> Option { } } -fn get_iden(lexer: &mut Lexer) -> String { +fn get_ident(lexer: &mut Lexer) -> String { lexer.slice().to_owned() } diff --git a/ablescript/src/parser.rs b/ablescript/src/parser.rs index f42873e..6f7ebb6 100644 --- a/ablescript/src/parser.rs +++ b/ablescript/src/parser.rs @@ -37,12 +37,7 @@ impl<'source> Parser<'source> { Token::Comment => continue, // T-Dark block (replace `lang` with `script`) - Token::TDark => { - self.tdark = true; - let mut block = self.get_block()?; - ast.append(&mut block.block); - self.tdark = false; - } + Token::TDark => ast.extend(self.tdark_flow()?.block), token => ast.push(self.parse(token)?), } } @@ -53,9 +48,16 @@ impl<'source> Parser<'source> { /// /// If EOF, return Error instead of None fn checked_next(&mut self) -> Result { - self.lexer - .next() - .ok_or_else(|| Error::unexpected_eof(self.lexer.span().start)) + loop { + match self + .lexer + .next() + .ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))? + { + Token::Comment => (), + token => break Ok(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::Loop => Ok(Stmt::new(self.loop_flow()?, start..self.lexer.span().end)), Token::Break => Ok(Stmt::new( - self.semi_terminated(StmtKind::Break)?, + self.semicolon_terminated(StmtKind::Break)?, start..self.lexer.span().end, )), Token::HopBack => Ok(Stmt::new( - self.semi_terminated(StmtKind::HopBack)?, + self.semicolon_terminated(StmtKind::HopBack)?, start..self.lexer.span().end, )), Token::Rlyeh => Ok(Stmt::new( - self.semi_terminated(StmtKind::Rlyeh)?, + self.semicolon_terminated(StmtKind::Rlyeh)?, start..self.lexer.span().end, )), Token::Rickroll => Ok(Stmt::new( - self.semi_terminated(StmtKind::Rickroll)?, + self.semicolon_terminated(StmtKind::Rickroll)?, start..self.lexer.span().end, )), @@ -98,6 +100,8 @@ impl<'source> Parser<'source> { | Token::Integer(_) | Token::Abool(_) | Token::Bool(_) + | Token::Nul + | Token::Not | Token::LeftBracket | Token::LeftParen => Ok(Stmt::new( self.value_flow(token)?, @@ -114,7 +118,7 @@ impl<'source> Parser<'source> { /// Require statement to be semicolon terminated /// /// Utility function for short statements - fn semi_terminated(&mut self, stmt_kind: StmtKind) -> Result { + fn semicolon_terminated(&mut self, stmt_kind: StmtKind) -> Result { self.require(Token::Semicolon)?; Ok(stmt_kind) } @@ -129,13 +133,13 @@ impl<'source> Parser<'source> { } /// Get an Identifier - fn get_iden(&mut self) -> Result { + fn get_ident(&mut self) -> Result { match self.checked_next()? { - Token::Identifier(iden) => Ok(Iden { - iden: if self.tdark { - iden.replace("lang", "script") + Token::Identifier(ident) => Ok(Ident { + ident: if self.tdark { + ident.replace("lang", "script") } else { - iden + ident }, span: self.lexer.span(), }), @@ -192,7 +196,7 @@ impl<'source> Parser<'source> { Token::LeftBracket => match buf.take() { Some(buf) => Ok(Expr::new( ExprKind::Index { - cart: Box::new(buf), + expr: Box::new(buf), index: Box::new(self.expr_flow(Token::RightBracket)?), }, start..self.lexer.span().end, @@ -208,9 +212,7 @@ impl<'source> Parser<'source> { | Token::EqualEqual | Token::NotEqual | Token::LessThan - | Token::GreaterThan - | Token::And - | Token::Or => Ok(Expr::new( + | Token::GreaterThan => Ok(Expr::new( self.binop_flow( BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?, buf, @@ -322,12 +324,21 @@ impl<'source> Parser<'source> { loop { match self.checked_next()? { Token::RightCurly => break, + Token::TDark => block.extend(self.tdark_flow()?.block), t => block.push(self.parse(t)?), } } Ok(Block { block }) } + /// Parse T-Dark block + fn tdark_flow(&mut self) -> Result { + self.tdark = true; + let block = self.get_block(); + self.tdark = false; + block + } + /// 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 /// 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(|| { Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span()) })?); - break self.semi_terminated(stmt)?; + break self.semicolon_terminated(stmt)?; } // Functio call @@ -355,26 +366,29 @@ impl<'source> Parser<'source> { // Variable Assignment Token::Equal => { - if let Some(Expr { - kind: ExprKind::Variable(iden), - span, - }) = buf - { + if let Some(Ok(assignable)) = buf.take().map(Assignable::from_expr) { break StmtKind::Assign { - iden: Iden::new(iden, span), + assignable, value: self.expr_flow(Token::Semicolon)?, }; + } else { + return Err(Error::new( + ErrorKind::UnexpectedToken(Token::Equal), + self.lexer.span(), + )); } } // Read input Token::Read => { - if let Some(Expr { - kind: ExprKind::Variable(iden), - span, - }) = buf - { - break self.semi_terminated(StmtKind::Read(Iden::new(iden, span)))?; + if let Some(Ok(assignable)) = buf.take().map(Assignable::from_expr) { + self.require(Token::Semicolon)?; + break StmtKind::Read(assignable); + } else { + return Err(Error::new( + ErrorKind::UnexpectedToken(Token::Read), + self.lexer.span(), + )); } } @@ -400,9 +414,9 @@ impl<'source> Parser<'source> { /// Parse functio flow /// - /// functio $iden (a, b, c) { ... } + /// functio $ident (a, b, c) { ... } fn functio_flow(&mut self) -> Result { - let iden = self.get_iden()?; + let ident = self.get_ident()?; self.require(Token::LeftParen)?; @@ -411,7 +425,7 @@ impl<'source> Parser<'source> { match self.checked_next()? { Token::RightParen => break, 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 match self.checked_next()? { @@ -431,14 +445,18 @@ impl<'source> Parser<'source> { let body = self.get_block()?; - Ok(StmtKind::Functio { iden, params, body }) + Ok(StmtKind::Functio { + ident, + params, + body, + }) } /// Parse BF function declaration /// - /// `bff $iden ([tapelen]) { ... }` + /// `bff $ident ([tapelen]) { ... }` fn bff_flow(&mut self) -> Result { - let iden = self.get_iden()?; + let ident = self.get_ident()?; let tape_len = match self.checked_next()? { Token::LeftParen => { @@ -472,7 +490,7 @@ impl<'source> Parser<'source> { } Ok(StmtKind::BfFunctio { - iden, + ident, tape_len, code, }) @@ -513,20 +531,20 @@ impl<'source> Parser<'source> { /// Parse variable declaration fn var_flow(&mut self) -> Result { - let iden = self.get_iden()?; + let ident = self.get_ident()?; let init = match self.checked_next()? { Token::Equal => Some(self.expr_flow(Token::Semicolon)?), Token::Semicolon => None, t => return Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())), }; - Ok(StmtKind::Var { iden, init }) + Ok(StmtKind::Var { ident, init }) } /// Parse Melo flow fn melo_flow(&mut self) -> Result { - let iden = self.get_iden()?; - self.semi_terminated(StmtKind::Melo(iden)) + let ident = self.get_ident()?; + self.semicolon_terminated(StmtKind::Melo(ident)) } /// Parse loop flow @@ -558,7 +576,7 @@ mod tests { rhs: Box::new(Expr { kind: ExprKind::BinOp { lhs: Box::new(Expr { - kind: ExprKind::Variable("a".to_string()), + kind: ExprKind::Variable("a".to_owned()), span: 5..6, }), rhs: Box::new(Expr { @@ -593,8 +611,8 @@ mod tests { let code = r#"var a = 42;"#; let expected = &[Stmt { kind: StmtKind::Var { - iden: Iden { - iden: "a".to_string(), + ident: Ident { + ident: "a".to_owned(), span: 4..5, }, init: Some(Expr { @@ -631,7 +649,7 @@ mod tests { body: Block { block: vec![Stmt { 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..46, @@ -650,18 +668,18 @@ mod tests { let code = r#"T-Dark { var lang = "lang" + lang; }"#; let expected = &[Stmt { kind: StmtKind::Var { - iden: Iden { - iden: "script".to_string(), + ident: Ident { + ident: "script".to_owned(), span: 13..17, }, init: Some(Expr { kind: ExprKind::BinOp { lhs: Box::new(Expr { - kind: ExprKind::Literal(Value::Str("script".to_string())), + kind: ExprKind::Literal(Value::Str("script".to_owned())), span: 20..26, }), rhs: Box::new(Expr { - kind: ExprKind::Variable("script".to_string()), + kind: ExprKind::Variable("script".to_owned()), span: 29..33, }), kind: BinOpKind::Add, @@ -684,7 +702,7 @@ mod tests { kind: ExprKind::Cart(vec![ ( Expr { - kind: ExprKind::Literal(Value::Str("able".to_string())), + kind: ExprKind::Literal(Value::Str("able".to_owned())), span: 1..7, }, Expr { @@ -694,7 +712,7 @@ mod tests { ), ( Expr { - kind: ExprKind::Literal(Value::Str("script".to_string())), + kind: ExprKind::Literal(Value::Str("script".to_owned())), span: 14..22, }, Expr { @@ -728,14 +746,14 @@ mod tests { let expected = &[Stmt { kind: StmtKind::Print(Expr { kind: ExprKind::Index { - cart: Box::new(Expr { + expr: Box::new(Expr { kind: ExprKind::Cart(vec![( Expr { - kind: ExprKind::Literal(Value::Str("able".to_string())), + kind: ExprKind::Literal(Value::Str("able".to_owned())), span: 1..7, }, Expr { - kind: ExprKind::Literal(Value::Str("ablecorp".to_string())), + kind: ExprKind::Literal(Value::Str("ablecorp".to_owned())), span: 11..21, }, )]), diff --git a/ablescript/src/variables.rs b/ablescript/src/variables.rs index f088b16..5d42a19 100644 --- a/ablescript/src/variables.rs +++ b/ablescript/src/variables.rs @@ -1,13 +1,13 @@ use std::{ - cell::RefCell, collections::HashMap, convert::TryFrom, fmt::Display, hash::Hash, io::Write, - mem::discriminant, rc::Rc, + cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, io::Write, mem::discriminant, + ops, rc::Rc, vec, }; 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 { Never = -1, Sometimes = 0, @@ -36,16 +36,43 @@ impl From for bool { #[derive(Debug, PartialEq, Clone, Hash)] pub enum Functio { - BfFunctio { + Bf { instructions: Vec, tape_len: usize, }, - AbleFunctio { + Able { params: Vec, body: Vec, }, + 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>>; + #[derive(Debug, Clone)] pub enum Value { Nul, @@ -54,7 +81,13 @@ pub enum Value { Bool(bool), Abool(Abool), Functio(Functio), - Cart(HashMap>>), + Cart(Cart), +} + +impl Default for Value { + fn default() -> Self { + Self::Nul + } } 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 { /// Write an AbleScript value to a Brainfuck input stream by /// coercing the value to an integer, then truncating that integer @@ -98,73 +113,668 @@ impl Value { /// any IO errors will cause a panic. pub fn bf_write(&self, stream: &mut impl Write) { stream - .write_all(&[self.clone().to_i32() as u8]) + .write_all(&[self.clone().into_i32() as u8]) .expect("Failed to write to Brainfuck input"); } /// Coerce a value to an integer. - pub fn to_i32(&self) -> i32 { + pub fn into_i32(self) -> i32 { match self { - Value::Abool(a) => *a as _, - Value::Bool(b) => *b as _, + Value::Abool(a) => a as _, + Value::Bool(b) => b as _, 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, tape_len, } => (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::Str(text) => text.parse().unwrap_or(consts::ANSWER), Value::Cart(c) => c.len() as _, } } - /// Coerce a Value to a boolean. The conversion cannot fail. - pub fn to_bool(&self) -> bool { + /// Coerce a value to a boolean. + pub fn into_bool(self) -> bool { match self { - Value::Abool(b) => (*b).into(), - Value::Bool(b) => *b, + Value::Abool(b) => b.into(), + Value::Bool(b) => b, Value::Functio(_) => true, - Value::Int(x) => *x != 0, - Value::Nul => true, - Value::Str(s) => !s.is_empty(), + Value::Int(x) => x != 0, + Value::Nul => false, + 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(), } } - /// Index a value with another value, as in the "a[b]" syntax. - pub fn index(&self, index: &Value) -> Rc> { - Rc::new(RefCell::new(match self { - Value::Nul => Value::Nul, - Value::Str(s) => Value::Int( - usize::try_from(index.to_i32() - 1) - .ok() - .and_then(|idx| s.as_bytes().get(idx).cloned()) - .map(|value| value as i32) - .unwrap_or(0), - ), - Value::Int(i) => Value::Int( - usize::try_from(index.to_i32() - 1) - .ok() - .and_then(|idx| format!("{}", i).as_bytes().get(idx).cloned()) - .map(|value| value as i32) - .unwrap_or(0), - ), - Value::Bool(b) => Value::Int( - usize::try_from(index.to_i32() - 1) - .ok() - .and_then(|idx| format!("{}", b).as_bytes().get(idx).cloned()) - .map(|value| value as i32) - .unwrap_or(0), - ), - Value::Abool(b) => Value::Int(*b as i32), - Value::Functio(_) => Value::Int(42), - Value::Cart(c) => { - return (c.get(index).cloned()).unwrap_or_else(|| Rc::new(RefCell::new(Value::Nul))) + /// Coerce a value to an aboolean. + pub fn into_abool(self) -> Abool { + match self { + Value::Nul => Abool::Never, + Value::Str(s) => match s.to_lowercase().as_str() { + "never" => Abool::Never, + "sometimes" => Abool::Sometimes, + "always" => Abool::Always, + s => { + if s.is_empty() { + Abool::Never + } else { + Abool::Always + } + } + }, + Value::Int(x) => match x.cmp(&0) { + std::cmp::Ordering::Less => Abool::Never, + std::cmp::Ordering::Equal => Abool::Sometimes, + std::cmp::Ordering::Greater => Abool::Always, + }, + Value::Bool(b) => { + if b { + Abool::Always + } else { + Abool::Never + } } - })) + Value::Abool(a) => a, + Value::Functio(f) => match f { + Functio::Bf { + instructions, + tape_len, + } => Value::Int( + (instructions.iter().map(|x| *x as usize).sum::() * 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::()) + } + 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::>(); + 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::>() + .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 { + 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::Abool(v) => write!(f, "{}", v), Value::Functio(v) => match v { - Functio::BfFunctio { + Functio::Bf { instructions, tape_len, } => { @@ -189,7 +799,7 @@ impl Display for Value { .expect("Brainfuck functio source should be UTF-8") ) } - Functio::AbleFunctio { params, body } => { + Functio::Able { params, body } => { write!( f, "({}) -> {:?}", @@ -199,12 +809,34 @@ impl Display for Value { 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) => { write!(f, "[")?; + let mut cart_vec = c.iter().collect::>(); + cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less)); - for (key, value) in c { - write!(f, "{} <= {},", value.borrow(), key)?; + for (idx, (key, value)) in cart_vec.into_iter().enumerate() { + write!( + f, + "{}{} <= {}", + if idx != 0 { ", " } else { "" }, + value.borrow(), + key + )?; } write!(f, "]")