diff --git a/ablescript/src/ast.rs b/ablescript/src/ast.rs index a5f19055..90432830 100644 --- a/ablescript/src/ast.rs +++ b/ablescript/src/ast.rs @@ -15,71 +15,26 @@ use crate::variables::Value; type Span = std::ops::Range; #[derive(Debug, Clone)] -pub struct Ident { - pub ident: String, +pub struct Iden { + pub iden: String, pub span: Span, } -impl Ident { - pub fn new(ident: String, span: Span) -> Self { - Self { ident, span } +impl Iden { + pub fn new(iden: String, span: Span) -> Self { + Self { iden, span } } } -impl PartialEq for Ident { +impl PartialEq for Iden { fn eq(&self, other: &Self) -> bool { - self.ident == other.ident + self.iden == other.iden } } -impl Hash for Ident { +impl Hash for Iden { fn hash(&self, state: &mut H) { - 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 }, - }) + self.iden.hash(state) } } @@ -121,21 +76,21 @@ pub enum StmtKind { HopBack, Var { - ident: Ident, + iden: Iden, init: Option, }, Assign { - assignable: Assignable, + iden: Iden, value: Expr, }, Functio { - ident: Ident, - params: Vec, + iden: Iden, + params: Vec, body: Block, }, BfFunctio { - ident: Ident, + iden: Iden, tape_len: Option, code: Vec, }, @@ -144,8 +99,8 @@ pub enum StmtKind { args: Vec, }, Print(Expr), - Read(Assignable), - Melo(Ident), + Read(Iden), + Melo(Iden), Rlyeh, Rickroll, } @@ -187,7 +142,7 @@ pub enum ExprKind { Literal(Value), Cart(Vec<(Expr, Expr)>), Index { - expr: Box, + cart: Box, index: Box, }, Variable(String), @@ -209,6 +164,8 @@ pub enum BinOpKind { Less, Equal, NotEqual, + And, + Or, } impl BinOpKind { @@ -224,6 +181,8 @@ 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 a8222273..fa612ebd 100644 --- a/ablescript/src/base_55.rs +++ b/ablescript/src/base_55.rs @@ -60,6 +60,68 @@ 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 91c7d2e4..16cb25e0 100644 --- a/ablescript/src/brian.rs +++ b/ablescript/src/brian.rs @@ -18,9 +18,6 @@ #![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 5c9cae6c..1f0f0f65 100644 --- a/ablescript/src/interpret.rs +++ b/ablescript/src/interpret.rs @@ -11,7 +11,6 @@ use std::{ cell::RefCell, collections::{HashMap, VecDeque}, io::{stdin, stdout, Read, Write}, - mem::take, ops::Range, process::exit, rc::Rc, @@ -20,9 +19,9 @@ use std::{ use rand::random; use crate::{ - ast::{Assignable, AssignableKind, Expr, ExprKind, Ident, Stmt, StmtKind}, + ast::{Expr, ExprKind, Iden, Stmt, StmtKind}, base_55, - consts::ablescript_consts, + consts::{self, ablescript_consts}, error::{Error, ErrorKind}, variables::{Functio, Value, Variable}, }; @@ -135,23 +134,66 @@ 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 { - 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), + // 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) + } } } - Not(expr) => !self.eval_expr(expr)?, + Not(expr) => Bool(!self.eval_expr(expr)?.to_bool()), Literal(value) => value.clone(), ExprKind::Cart(members) => Value::Cart( members @@ -164,21 +206,18 @@ impl ExecEnv { }) .collect::, _>>()?, ), - Index { expr, index } => { - let value = self.eval_expr(expr)?; + Index { cart, index } => { + let cart = self.eval_expr(cart)?; let index = self.eval_expr(index)?; - value - .into_cart() - .get(&index) - .map(|x| x.borrow().clone()) - .unwrap_or(Value::Nul) + // TODO: this probably shouldn't be cloned + cart.index(&index).borrow().clone() } // TODO: not too happy with constructing an artificial - // Ident here. - Variable(name) => self.get_var(&Ident { - ident: name.to_owned(), + // Iden here. + Variable(name) => self.get_var(&Iden { + iden: name.to_owned(), span: expr.span.clone(), })?, }) @@ -190,52 +229,52 @@ impl ExecEnv { StmtKind::Print(expr) => { println!("{}", self.eval_expr(expr)?); } - StmtKind::Var { ident, init } => { + StmtKind::Var { iden, init } => { let init = match init { Some(e) => self.eval_expr(e)?, None => Value::Nul, }; - self.decl_var(&ident.ident, init); + self.decl_var(&iden.iden, init); } - StmtKind::Functio { - ident, - params, - body, - } => { + StmtKind::Functio { iden, params, body } => { self.decl_var( - &ident.ident, + &iden.iden, Value::Functio(Functio::AbleFunctio { - params: params.iter().map(|ident| ident.ident.to_owned()).collect(), + params: params.iter().map(|iden| iden.iden.to_string()).collect(), body: body.block.to_owned(), }), ); } StmtKind::BfFunctio { - ident, + iden, tape_len, code, } => { self.decl_var( - &ident.ident, + &iden.iden, 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.into_i32() as usize)) + .map(|tape_len| self.eval_expr(tape_len).map(|v| v.to_i32() as usize)) .unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?, }), ); } StmtKind::If { cond, body } => { - if self.eval_expr(cond)?.into_bool() { + if self.eval_expr(cond)?.to_bool() { return self.eval_stmts_hs(&body.block, true); } } StmtKind::Call { expr, args } => { - let func = self.eval_expr(expr)?.into_functio(); + let func = self.eval_expr(expr)?; - self.fn_call(func, args, &stmt.span)?; + if let Value::Functio(func) = func { + self.fn_call(func, args, &stmt.span)?; + } else { + // Fail silently for now. + } } StmtKind::Loop { body } => loop { let res = self.eval_stmts_hs(&body.block, true)?; @@ -245,8 +284,9 @@ impl ExecEnv { HaltStatus::Hopback(_) => continue, } }, - StmtKind::Assign { assignable, value } => { - self.assign(assignable, self.eval_expr(value)?)?; + StmtKind::Assign { iden, value } => { + let value = self.eval_expr(value)?; + self.get_var_mut(iden)?.value.replace(value); } StmtKind::Break => { return Ok(HaltStatus::Break(stmt.span.clone())); @@ -254,8 +294,8 @@ impl ExecEnv { StmtKind::HopBack => { return Ok(HaltStatus::Hopback(stmt.span.clone())); } - StmtKind::Melo(ident) => { - self.get_var_mut(ident)?.melo = true; + StmtKind::Melo(iden) => { + self.get_var_mut(iden)?.melo = true; } StmtKind::Rlyeh => { // Maybe print a creepy error message or something @@ -267,69 +307,20 @@ impl ExecEnv { .write_all(include_str!("rickroll").as_bytes()) .expect("Failed to write to stdout"); } - StmtKind::Read(assignable) => { + StmtKind::Read(iden) => { let mut value = 0; for _ in 0..READ_BITS { value <<= 1; value += self.get_bit()? as i32; } - self.assign(assignable, Value::Int(value))?; + self.get_var_mut(iden)?.value.replace(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`. @@ -340,8 +331,8 @@ impl ExecEnv { .iter() .map(|arg| { if let ExprKind::Variable(name) = &arg.kind { - self.get_var_rc(&Ident { - ident: name.to_owned(), + self.get_var_rc(&Iden { + iden: name.to_owned(), span: arg.span.clone(), }) } else { @@ -396,17 +387,6 @@ 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(()) } @@ -433,9 +413,9 @@ impl ExecEnv { /// Get the value of a variable. Throw an error if the variable is /// inaccessible or banned. - fn get_var(&self, name: &Ident) -> Result { + fn get_var(&self, name: &Iden) -> Result { // One-letter names are reserved as base55 numbers. - let mut chars = name.ident.chars(); + let mut chars = name.iden.chars(); if let (Some(first), None) = (chars.next(), chars.next()) { return Ok(Value::Int(base_55::char2num(first))); } @@ -446,20 +426,20 @@ impl ExecEnv { .stack .iter() .rev() - .find_map(|scope| scope.variables.get(&name.ident)) + .find_map(|scope| scope.variables.get(&name.iden)) { Some(var) => { if !var.melo { Ok(var.value.borrow().clone()) } else { Err(Error { - kind: ErrorKind::MeloVariable(name.ident.to_owned()), + kind: ErrorKind::MeloVariable(name.iden.to_owned()), span: name.span.clone(), }) } } None => Err(Error { - kind: ErrorKind::UnknownVariable(name.ident.to_owned()), + kind: ErrorKind::UnknownVariable(name.iden.to_owned()), span: name.span.clone(), }), } @@ -467,27 +447,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: &Ident) -> Result<&mut Variable, Error> { + fn get_var_mut(&mut self, name: &Iden) -> 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.ident)) + .find_map(|scope| scope.variables.get_mut(&name.iden)) { Some(var) => { if !var.melo { Ok(var) } else { Err(Error { - kind: ErrorKind::MeloVariable(name.ident.to_owned()), + kind: ErrorKind::MeloVariable(name.iden.to_owned()), span: name.span.clone(), }) } } None => Err(Error { - kind: ErrorKind::UnknownVariable(name.ident.to_owned()), + kind: ErrorKind::UnknownVariable(name.iden.to_owned()), span: name.span.clone(), }), } @@ -495,7 +475,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: &Ident) -> Result>, Error> { + fn get_var_rc(&mut self, name: &Iden) -> Result>, Error> { Ok(self.get_var_mut(name)?.value.clone()) } @@ -591,7 +571,7 @@ mod tests { span: 1..1 }) .unwrap(), - Value::Int(-2147483648) + Value::Int(42) ); // And the same for divide by zero. @@ -599,7 +579,7 @@ mod tests { env.eval_expr(&Expr { kind: ExprKind::BinOp { lhs: Box::new(Expr { - kind: ExprKind::Literal(Value::Int(84)), + kind: ExprKind::Literal(Value::Int(1)), span: 1..1, }), rhs: Box::new(Expr { @@ -611,7 +591,7 @@ mod tests { span: 1..1 }) .unwrap(), - Value::Int(2) + Value::Int(42) ); } @@ -637,8 +617,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(&Ident { - ident: "bar".to_owned(), + env.get_var(&Iden { + iden: "bar".to_owned(), span: 1..1, }) .unwrap(), @@ -648,8 +628,8 @@ mod tests { // Assigning an existing variable. eval(&mut env, "foo = \"hi\";").unwrap(); assert_eq!( - env.get_var(&Ident { - ident: "foo".to_owned(), + env.get_var(&Iden { + iden: "foo".to_owned(), span: 1..1, }) .unwrap(), @@ -674,8 +654,8 @@ mod tests { .unwrap(); assert_eq!( - env.get_var(&Ident { - ident: "foo".to_owned(), + env.get_var(&Iden { + iden: "foo".to_owned(), span: 1..1, }) .unwrap(), diff --git a/ablescript/src/lexer.rs b/ablescript/src/lexer.rs index a6c5940b..c622800d 100644 --- a/ablescript/src/lexer.rs +++ b/ablescript/src/lexer.rs @@ -64,7 +64,13 @@ pub enum Token { #[token("!=")] NotEqual, - #[regex("!|aint")] // also add aint as a not keyword + #[token("&")] + And, + + #[token("|")] + Or, + + #[token("!|aint")] // also add aint as a not keyword Not, // Keywords @@ -117,7 +123,7 @@ pub enum Token { // Literals /// True, False - #[regex("true|false", get_value)] + #[regex("true|false", get_bool)] Bool(bool), /// Always, Sometimes, Never @@ -133,11 +139,11 @@ pub enum Token { String(String), /// Integer - #[regex(r"-?[0-9]+", get_value)] + #[regex(r"-?[0-9]+", get_int)] Integer(i32), /// A C-complaint identifier - #[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_ident)] + #[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)] Identifier(String), #[regex(r"owo .*")] @@ -151,7 +157,11 @@ pub enum Token { Error, } -fn get_value(lexer: &mut Lexer) -> Option { +fn get_bool(lexer: &mut Lexer) -> Option { + lexer.slice().parse().ok() +} + +fn get_int(lexer: &mut Lexer) -> Option { lexer.slice().parse().ok() } @@ -168,7 +178,7 @@ fn get_abool(lexer: &mut Lexer) -> Option { } } -fn get_ident(lexer: &mut Lexer) -> String { +fn get_iden(lexer: &mut Lexer) -> String { lexer.slice().to_owned() } diff --git a/ablescript/src/parser.rs b/ablescript/src/parser.rs index 4f8bf235..f42873e5 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.semicolon_terminated(StmtKind::Break)?, + self.semi_terminated(StmtKind::Break)?, start..self.lexer.span().end, )), Token::HopBack => Ok(Stmt::new( - self.semicolon_terminated(StmtKind::HopBack)?, + self.semi_terminated(StmtKind::HopBack)?, start..self.lexer.span().end, )), Token::Rlyeh => Ok(Stmt::new( - self.semicolon_terminated(StmtKind::Rlyeh)?, + self.semi_terminated(StmtKind::Rlyeh)?, start..self.lexer.span().end, )), Token::Rickroll => Ok(Stmt::new( - self.semicolon_terminated(StmtKind::Rickroll)?, + self.semi_terminated(StmtKind::Rickroll)?, start..self.lexer.span().end, )), @@ -98,8 +98,6 @@ 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)?, @@ -116,7 +114,7 @@ impl<'source> Parser<'source> { /// Require statement to be semicolon terminated /// /// Utility function for short statements - fn semicolon_terminated(&mut self, stmt_kind: StmtKind) -> Result { + fn semi_terminated(&mut self, stmt_kind: StmtKind) -> Result { self.require(Token::Semicolon)?; Ok(stmt_kind) } @@ -131,13 +129,13 @@ impl<'source> Parser<'source> { } /// Get an Identifier - fn get_ident(&mut self) -> Result { + fn get_iden(&mut self) -> Result { match self.checked_next()? { - Token::Identifier(ident) => Ok(Ident { - ident: if self.tdark { - ident.replace("lang", "script") + Token::Identifier(iden) => Ok(Iden { + iden: if self.tdark { + iden.replace("lang", "script") } else { - ident + iden }, span: self.lexer.span(), }), @@ -194,7 +192,7 @@ impl<'source> Parser<'source> { Token::LeftBracket => match buf.take() { Some(buf) => Ok(Expr::new( ExprKind::Index { - expr: Box::new(buf), + cart: Box::new(buf), index: Box::new(self.expr_flow(Token::RightBracket)?), }, start..self.lexer.span().end, @@ -210,7 +208,9 @@ impl<'source> Parser<'source> { | Token::EqualEqual | Token::NotEqual | Token::LessThan - | Token::GreaterThan => Ok(Expr::new( + | Token::GreaterThan + | Token::And + | Token::Or => 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.semicolon_terminated(stmt)?; + break self.semi_terminated(stmt)?; } // Functio call @@ -355,29 +355,26 @@ impl<'source> Parser<'source> { // Variable Assignment Token::Equal => { - if let Some(Ok(assignable)) = buf.take().map(Assignable::from_expr) { + if let Some(Expr { + kind: ExprKind::Variable(iden), + span, + }) = buf + { break StmtKind::Assign { - assignable, + iden: Iden::new(iden, span), 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(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(), - )); + if let Some(Expr { + kind: ExprKind::Variable(iden), + span, + }) = buf + { + break self.semi_terminated(StmtKind::Read(Iden::new(iden, span)))?; } } @@ -403,9 +400,9 @@ impl<'source> Parser<'source> { /// Parse functio flow /// - /// functio $ident (a, b, c) { ... } + /// functio $iden (a, b, c) { ... } fn functio_flow(&mut self) -> Result { - let ident = self.get_ident()?; + let iden = self.get_iden()?; self.require(Token::LeftParen)?; @@ -414,7 +411,7 @@ impl<'source> Parser<'source> { match self.checked_next()? { Token::RightParen => break, Token::Identifier(i) => { - params.push(Ident::new(i, self.lexer.span())); + params.push(Iden::new(i, self.lexer.span())); // Require comma (next) or right paren (end) after identifier match self.checked_next()? { @@ -434,18 +431,14 @@ impl<'source> Parser<'source> { let body = self.get_block()?; - Ok(StmtKind::Functio { - ident, - params, - body, - }) + Ok(StmtKind::Functio { iden, params, body }) } /// Parse BF function declaration /// - /// `bff $ident ([tapelen]) { ... }` + /// `bff $iden ([tapelen]) { ... }` fn bff_flow(&mut self) -> Result { - let ident = self.get_ident()?; + let iden = self.get_iden()?; let tape_len = match self.checked_next()? { Token::LeftParen => { @@ -479,7 +472,7 @@ impl<'source> Parser<'source> { } Ok(StmtKind::BfFunctio { - ident, + iden, tape_len, code, }) @@ -520,20 +513,20 @@ impl<'source> Parser<'source> { /// Parse variable declaration fn var_flow(&mut self) -> Result { - let ident = self.get_ident()?; + let iden = self.get_iden()?; 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 { ident, init }) + Ok(StmtKind::Var { iden, init }) } /// Parse Melo flow fn melo_flow(&mut self) -> Result { - let ident = self.get_ident()?; - self.semicolon_terminated(StmtKind::Melo(ident)) + let iden = self.get_iden()?; + self.semi_terminated(StmtKind::Melo(iden)) } /// Parse loop flow @@ -565,7 +558,7 @@ mod tests { rhs: Box::new(Expr { kind: ExprKind::BinOp { lhs: Box::new(Expr { - kind: ExprKind::Variable("a".to_owned()), + kind: ExprKind::Variable("a".to_string()), span: 5..6, }), rhs: Box::new(Expr { @@ -600,8 +593,8 @@ mod tests { let code = r#"var a = 42;"#; let expected = &[Stmt { kind: StmtKind::Var { - ident: Ident { - ident: "a".to_owned(), + iden: Iden { + iden: "a".to_string(), span: 4..5, }, init: Some(Expr { @@ -638,7 +631,7 @@ mod tests { body: Block { block: vec![Stmt { kind: StmtKind::Print(Expr { - kind: ExprKind::Literal(Value::Str("Buy Able products!".to_owned())), + kind: ExprKind::Literal(Value::Str("Buy Able products!".to_string())), span: 19..39, }), span: 19..46, @@ -657,18 +650,18 @@ mod tests { let code = r#"T-Dark { var lang = "lang" + lang; }"#; let expected = &[Stmt { kind: StmtKind::Var { - ident: Ident { - ident: "script".to_owned(), + iden: Iden { + iden: "script".to_string(), span: 13..17, }, init: Some(Expr { kind: ExprKind::BinOp { lhs: Box::new(Expr { - kind: ExprKind::Literal(Value::Str("script".to_owned())), + kind: ExprKind::Literal(Value::Str("script".to_string())), span: 20..26, }), rhs: Box::new(Expr { - kind: ExprKind::Variable("script".to_owned()), + kind: ExprKind::Variable("script".to_string()), span: 29..33, }), kind: BinOpKind::Add, @@ -691,7 +684,7 @@ mod tests { kind: ExprKind::Cart(vec![ ( Expr { - kind: ExprKind::Literal(Value::Str("able".to_owned())), + kind: ExprKind::Literal(Value::Str("able".to_string())), span: 1..7, }, Expr { @@ -701,7 +694,7 @@ mod tests { ), ( Expr { - kind: ExprKind::Literal(Value::Str("script".to_owned())), + kind: ExprKind::Literal(Value::Str("script".to_string())), span: 14..22, }, Expr { @@ -735,14 +728,14 @@ mod tests { let expected = &[Stmt { kind: StmtKind::Print(Expr { kind: ExprKind::Index { - expr: Box::new(Expr { + cart: Box::new(Expr { kind: ExprKind::Cart(vec![( Expr { - kind: ExprKind::Literal(Value::Str("able".to_owned())), + kind: ExprKind::Literal(Value::Str("able".to_string())), span: 1..7, }, Expr { - kind: ExprKind::Literal(Value::Str("ablecorp".to_owned())), + kind: ExprKind::Literal(Value::Str("ablecorp".to_string())), span: 11..21, }, )]), diff --git a/ablescript/src/variables.rs b/ablescript/src/variables.rs index 5ee99cb6..f088b165 100644 --- a/ablescript/src/variables.rs +++ b/ablescript/src/variables.rs @@ -1,13 +1,13 @@ use std::{ - cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, io::Write, mem::discriminant, - ops, rc::Rc, vec, + cell::RefCell, collections::HashMap, convert::TryFrom, fmt::Display, hash::Hash, io::Write, + mem::discriminant, rc::Rc, }; use rand::Rng; use crate::{ast::Stmt, consts}; -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Abool { Never = -1, Sometimes = 0, @@ -44,11 +44,8 @@ pub enum Functio { params: Vec, body: Vec, }, - Eval(String), } -pub type Cart = HashMap>>; - #[derive(Debug, Clone)] pub enum Value { Nul, @@ -57,13 +54,7 @@ pub enum Value { Bool(bool), Abool(Abool), Functio(Functio), - Cart(Cart), -} - -impl Default for Value { - fn default() -> Self { - Self::Nul - } + Cart(HashMap>>), } impl Hash for Value { @@ -81,6 +72,24 @@ 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 @@ -89,408 +98,73 @@ impl Value { /// any IO errors will cause a panic. pub fn bf_write(&self, stream: &mut impl Write) { stream - .write_all(&[self.clone().into_i32() as u8]) + .write_all(&[self.clone().to_i32() as u8]) .expect("Failed to write to Brainfuck input"); } /// Coerce a value to an integer. - pub fn into_i32(self) -> i32 { + pub fn to_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() + format!("{:?}", body).len()) as _ - } - Functio::Eval(s) => s.len() as _, + Functio::AbleFunctio { params, body } => (params.len() + body.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. - pub fn into_bool(self) -> bool { + /// Coerce a Value to a boolean. The conversion cannot fail. + pub fn to_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 => false, - Value::Str(s) => match s.to_lowercase().as_str() { - "false" | "no" | "🇳🇴" => false, - "true" | "yes" => true, - s => !s.is_empty(), - }, + Value::Int(x) => *x != 0, + Value::Nul => true, + Value::Str(s) => !s.is_empty(), Value::Cart(c) => !c.is_empty(), } } - /// 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 { + /// 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::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(), + 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), ), - } - } -} - -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::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))) } - 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())), - } + })) } } @@ -525,21 +199,12 @@ 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 (idx, (key, value)) in cart_vec.into_iter().enumerate() { - write!( - f, - "{}{} <= {}", - if idx != 0 { ", " } else { "" }, - value.borrow(), - key - )?; + for (key, value) in c { + write!(f, "{} <= {},", value.borrow(), key)?; } write!(f, "]")