diff --git a/ablescript/src/ast.rs b/ablescript/src/ast.rs index 90432830..a5f19055 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 fa612ebd..a8222273 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 16cb25e0..91c7d2e4 100644 --- a/ablescript/src/brian.rs +++ b/ablescript/src/brian.rs @@ -18,6 +18,9 @@ #![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, error::Error, diff --git a/ablescript/src/interpret.rs b/ablescript/src/interpret.rs index 1f0f0f65..5c9cae6c 100644 --- a/ablescript/src/interpret.rs +++ b/ablescript/src/interpret.rs @@ -11,6 +11,7 @@ use std::{ cell::RefCell, collections::{HashMap, VecDeque}, io::{stdin, stdout, Read, Write}, + mem::take, ops::Range, process::exit, rc::Rc, @@ -19,9 +20,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 +135,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 +164,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 +190,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, + &ident.ident, Value::Functio(Functio::AbleFunctio { - 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(), }), ); } StmtKind::BfFunctio { - iden, + ident, tape_len, code, } => { self.decl_var( - &iden.iden, + &ident.ident, Value::Functio(Functio::BfFunctio { 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 +245,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 +254,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 +267,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 +340,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 { @@ -387,6 +396,17 @@ impl ExecEnv { self.stack.pop(); res?; } + 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(()) } @@ -413,9 +433,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 +446,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 +467,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 +495,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 +591,7 @@ mod tests { span: 1..1 }) .unwrap(), - Value::Int(42) + Value::Int(-2147483648) ); // And the same for divide by zero. @@ -579,7 +599,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 +611,7 @@ mod tests { span: 1..1 }) .unwrap(), - Value::Int(42) + Value::Int(2) ); } @@ -617,8 +637,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 +648,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 +674,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 c622800d..a6c5940b 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 f42873e5..4f8bf235 100644 --- a/ablescript/src/parser.rs +++ b/ablescript/src/parser.rs @@ -76,19 +76,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 +98,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 +116,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 +131,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 +194,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 +210,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, @@ -340,7 +340,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 +355,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 +403,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 +414,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 +434,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 +479,7 @@ impl<'source> Parser<'source> { } Ok(StmtKind::BfFunctio { - iden, + ident, tape_len, code, }) @@ -513,20 +520,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 +565,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 +600,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 +638,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 +657,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 +691,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 +701,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 +735,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 f088b165..5ee99cb6 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}; -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] pub enum Abool { Never = -1, Sometimes = 0, @@ -44,8 +44,11 @@ pub enum Functio { params: Vec, body: Vec, }, + Eval(String), } +pub type Cart = HashMap>>; + #[derive(Debug, Clone)] pub enum Value { Nul, @@ -54,7 +57,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 +81,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 +89,408 @@ 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 { + // 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::BfFunctio { instructions, tape_len, } => (instructions.len() + tape_len) as _, - Functio::AbleFunctio { params, body } => (params.len() + body.len()) as _, + Functio::AbleFunctio { params, body } => { + (params.len() + format!("{:?}", body).len()) as _ + } + 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(_) => todo!(), + 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::AbleFunctio { + body: vec![], + params: vec![], + }, + Value::Str(s) => Functio::Eval(s), + Value::Int(_) => todo!(), + Value::Bool(_) => todo!(), + Value::Abool(_) => todo!(), + Value::Functio(f) => f, + Value::Cart(_) => todo!(), + } + } + + /// 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::AbleFunctio { 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::BfFunctio { + 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::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(_) => todo!(), + 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(_) => todo!(), + 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(_) => todo!(), + 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(_) => todo!(), + 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(_) => todo!(), + 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())), + } } } @@ -199,12 +525,21 @@ impl Display for Value { body, ) } + 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, "]")