Release 0.2

Reviewed-on: https://git.ablecorp.us:443/AbleScript/able-script/pulls/1
This commit is contained in:
Alex Bethel 2021-12-14 22:31:52 +00:00
commit 0451e71104
9 changed files with 1092 additions and 342 deletions

View file

@ -0,0 +1,44 @@
functio arity_0() {
"this function has arity 0" print;
}
functio arity_1(arg1) {
"this function has arity 1" print;
arg1 print;
}
functio arity_2(arg1, arg2) {
"this function has arity 2" print;
arg1 print;
arg2 print;
}
functio arity_3(arg1, arg2, arg3) {
"this function has arity 3" print;
arg1 print;
arg2 print;
arg3 print;
}
owo arity_0();
owo arity_1("foo");
owo arity_2("foo", "bar");
owo arity_3("foo", "bar", "baz");
var i1 = arity_0 * arity_1;
i1("second");
"----" print;
var i2 = arity_1 * arity_0;
i2("first");
"----" print;
var ifancy = arity_3 * arity_3;
ifancy("left1", "right1", "left2", "right2", "left3", "right3");
"----" print;
var another = arity_0 * arity_3;
another("right1", "right2", "right3");

View file

@ -1,5 +1,5 @@
functio helloable() { functio helloable() {
"Hello, Able!" print; "Hello, Able!" print;
} }
var cart = ["able" <= 42, helloable <= "hello"]; var cart = ["able" <= 42, helloable <= "hello"];

View file

@ -15,26 +15,71 @@ use crate::variables::Value;
type Span = std::ops::Range<usize>; type Span = std::ops::Range<usize>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Iden { pub struct Ident {
pub iden: String, pub ident: String,
pub span: Span, pub span: Span,
} }
impl Iden { impl Ident {
pub fn new(iden: String, span: Span) -> Self { pub fn new(ident: String, span: Span) -> Self {
Self { iden, span } Self { ident, span }
} }
} }
impl PartialEq for Iden { impl PartialEq for Ident {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.iden == other.iden self.ident == other.ident
} }
} }
impl Hash for Iden { impl Hash for Ident {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.iden.hash(state) self.ident.hash(state)
}
}
#[derive(Debug, PartialEq, Clone, Hash)]
pub struct Assignable {
pub ident: Ident,
pub kind: AssignableKind,
}
#[derive(Debug, PartialEq, Clone, Hash)]
pub enum AssignableKind {
Variable,
Index { indices: Vec<Expr> },
}
impl Assignable {
pub fn from_expr(expr: Expr) -> Result<Assignable, ()> {
match expr.kind {
ExprKind::Variable(ident) => Ok(Assignable {
ident: Ident::new(ident, expr.span),
kind: AssignableKind::Variable,
}),
ExprKind::Index { expr, index } => Self::from_index(*expr, *index),
_ => Err(()),
}
}
fn from_index(mut buf: Expr, index: Expr) -> Result<Assignable, ()> {
let mut indices = vec![index];
let ident = loop {
match buf.kind {
ExprKind::Variable(ident) => break ident,
ExprKind::Index { expr, index } => {
indices.push(*index);
buf = *expr;
}
_ => return Err(()),
}
};
indices.reverse();
Ok(Assignable {
ident: Ident::new(ident, buf.span),
kind: AssignableKind::Index { indices },
})
} }
} }
@ -76,21 +121,21 @@ pub enum StmtKind {
HopBack, HopBack,
Var { Var {
iden: Iden, ident: Ident,
init: Option<Expr>, init: Option<Expr>,
}, },
Assign { Assign {
iden: Iden, assignable: Assignable,
value: Expr, value: Expr,
}, },
Functio { Functio {
iden: Iden, ident: Ident,
params: Vec<Iden>, params: Vec<Ident>,
body: Block, body: Block,
}, },
BfFunctio { BfFunctio {
iden: Iden, ident: Ident,
tape_len: Option<Expr>, tape_len: Option<Expr>,
code: Vec<u8>, code: Vec<u8>,
}, },
@ -99,8 +144,8 @@ pub enum StmtKind {
args: Vec<Expr>, args: Vec<Expr>,
}, },
Print(Expr), Print(Expr),
Read(Iden), Read(Assignable),
Melo(Iden), Melo(Ident),
Rlyeh, Rlyeh,
Rickroll, Rickroll,
} }
@ -142,7 +187,7 @@ pub enum ExprKind {
Literal(Value), Literal(Value),
Cart(Vec<(Expr, Expr)>), Cart(Vec<(Expr, Expr)>),
Index { Index {
cart: Box<Expr>, expr: Box<Expr>,
index: Box<Expr>, index: Box<Expr>,
}, },
Variable(String), Variable(String),
@ -164,8 +209,6 @@ pub enum BinOpKind {
Less, Less,
Equal, Equal,
NotEqual, NotEqual,
And,
Or,
} }
impl BinOpKind { impl BinOpKind {
@ -181,8 +224,6 @@ impl BinOpKind {
Token::LessThan => Ok(Self::Less), Token::LessThan => Ok(Self::Less),
Token::EqualEqual => Ok(Self::Equal), Token::EqualEqual => Ok(Self::Equal),
Token::NotEqual => Ok(Self::NotEqual), Token::NotEqual => Ok(Self::NotEqual),
Token::And => Ok(Self::And),
Token::Or => Ok(Self::Or),
t => Err(crate::error::ErrorKind::UnexpectedToken(t)), t => Err(crate::error::ErrorKind::UnexpectedToken(t)),
} }
} }

View file

@ -60,68 +60,6 @@ pub fn char2num(character: char) -> i32 {
_ => 0, _ => 0,
} }
} }
pub fn num2char(number: i32) -> char {
match number {
-26 => 'Z',
-25 => 'Y',
-24 => 'X',
-23 => 'W',
-22 => 'V',
-210 => 'U',
-20 => 'T',
-18 => 'R',
-19 => 'S',
-17 => 'Q',
-16 => 'P',
-15 => 'O',
-14 => 'N',
-13 => 'M',
-12 => 'L',
-11 => 'K',
-10 => 'J',
-9 => 'I',
-8 => 'H',
-7 => 'G',
-6 => 'F',
-5 => 'E',
-4 => 'D',
-3 => 'C',
-2 => 'B',
-1 => 'A',
0 => ' ',
1 => 'a',
2 => 'b',
3 => 'c',
4 => 'd',
5 => 'e',
6 => 'f',
7 => 'g',
8 => 'h',
9 => 'i',
10 => 'j',
11 => 'k',
12 => 'l',
13 => 'm',
14 => 'n',
15 => 'o',
16 => 'p',
17 => 'q',
18 => 'r',
19 => 's',
20 => 't',
21 => 'u',
22 => 'v',
23 => 'w',
24 => 'x',
25 => 'y',
26 => 'z',
// NOTE(Able): Why does it jump to 53 here? MY REASONS ARE BEYOND YOUR UNDERSTANDING MORTAL
53 => '/',
54 => '\\',
55 => '.',
_ => ' ',
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -17,6 +17,8 @@
//! [`interpret_with_output`]: Interpreter::interpret_with_output //! [`interpret_with_output`]: Interpreter::interpret_with_output
#![deny(missing_docs)] #![deny(missing_docs)]
// Putting this here because we still don't use the entire capabilities of this module. ~~Alex
#![allow(dead_code)]
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
@ -30,6 +32,8 @@ use std::{
/// The default limit for the tape size. This is the value used by methods that don't take it as a parameter /// The default limit for the tape size. This is the value used by methods that don't take it as a parameter
pub const DEFAULT_TAPE_SIZE_LIMIT: usize = 30_000; pub const DEFAULT_TAPE_SIZE_LIMIT: usize = 30_000;
/// Mappings from integers to BF instructions
pub const INSTRUCTION_MAPPINGS: &[u8] = b"[]+-,.<>";
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
/// A brainfuck interpreter. Read the [module level documentation](self) for more /// A brainfuck interpreter. Read the [module level documentation](self) for more
pub struct Interpreter<'a, I> { pub struct Interpreter<'a, I> {

View file

@ -9,8 +9,10 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use std::{ use std::{
cell::RefCell, cell::RefCell,
cmp::Ordering,
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
io::{stdin, stdout, Read, Write}, io::{stdin, stdout, Read, Write},
mem::take,
ops::Range, ops::Range,
process::exit, process::exit,
rc::Rc, rc::Rc,
@ -19,9 +21,9 @@ use std::{
use rand::random; use rand::random;
use crate::{ use crate::{
ast::{Expr, ExprKind, Iden, Stmt, StmtKind}, ast::{Assignable, AssignableKind, Expr, ExprKind, Ident, Stmt, StmtKind},
base_55, base_55,
consts::{self, ablescript_consts}, consts::ablescript_consts,
error::{Error, ErrorKind}, error::{Error, ErrorKind},
variables::{Functio, Value, Variable}, variables::{Functio, Value, Variable},
}; };
@ -134,66 +136,23 @@ impl ExecEnv {
fn eval_expr(&self, expr: &Expr) -> Result<Value, Error> { fn eval_expr(&self, expr: &Expr) -> Result<Value, Error> {
use crate::ast::BinOpKind::*; use crate::ast::BinOpKind::*;
use crate::ast::ExprKind::*; use crate::ast::ExprKind::*;
use Value::*;
Ok(match &expr.kind { Ok(match &expr.kind {
BinOp { lhs, rhs, kind } => { BinOp { lhs, rhs, kind } => {
let lhs = self.eval_expr(lhs)?; let lhs = self.eval_expr(lhs)?;
let rhs = self.eval_expr(rhs)?; let rhs = self.eval_expr(rhs)?;
match kind { match kind {
// Arithmetic operators. Add => lhs + rhs,
Add | Subtract | Multiply | Divide => { Subtract => lhs - rhs,
let lhs = lhs.to_i32(); Multiply => lhs * rhs,
let rhs = rhs.to_i32(); Divide => lhs / rhs,
Greater => Value::Bool(lhs > rhs),
let res = match kind { Less => Value::Bool(lhs < rhs),
Add => lhs.checked_add(rhs), Equal => Value::Bool(lhs == rhs),
Subtract => lhs.checked_sub(rhs), NotEqual => Value::Bool(lhs != rhs),
Multiply => lhs.checked_mul(rhs),
Divide => lhs.checked_div(rhs),
_ => unreachable!(),
}
.unwrap_or(consts::ANSWER);
Int(res)
}
// Numeric comparisons.
Less | Greater => {
let lhs = lhs.to_i32();
let rhs = rhs.to_i32();
let res = match kind {
Less => lhs < rhs,
Greater => lhs > rhs,
_ => unreachable!(),
};
Bool(res)
}
// General comparisons.
Equal | NotEqual => {
let res = match kind {
Equal => lhs == rhs,
NotEqual => lhs != rhs,
_ => unreachable!(),
};
Bool(res)
}
// Logical connectives.
And | Or => {
let lhs = lhs.to_bool();
let rhs = rhs.to_bool();
let res = match kind {
And => lhs && rhs,
Or => lhs || rhs,
_ => unreachable!(),
};
Bool(res)
}
} }
} }
Not(expr) => Bool(!self.eval_expr(expr)?.to_bool()), Not(expr) => !self.eval_expr(expr)?,
Literal(value) => value.clone(), Literal(value) => value.clone(),
ExprKind::Cart(members) => Value::Cart( ExprKind::Cart(members) => Value::Cart(
members members
@ -206,18 +165,21 @@ impl ExecEnv {
}) })
.collect::<Result<HashMap<_, _>, _>>()?, .collect::<Result<HashMap<_, _>, _>>()?,
), ),
Index { cart, index } => { Index { expr, index } => {
let cart = self.eval_expr(cart)?; let value = self.eval_expr(expr)?;
let index = self.eval_expr(index)?; let index = self.eval_expr(index)?;
// TODO: this probably shouldn't be cloned value
cart.index(&index).borrow().clone() .into_cart()
.get(&index)
.map(|x| x.borrow().clone())
.unwrap_or(Value::Nul)
} }
// TODO: not too happy with constructing an artificial // TODO: not too happy with constructing an artificial
// Iden here. // Ident here.
Variable(name) => self.get_var(&Iden { Variable(name) => self.get_var(&Ident {
iden: name.to_owned(), ident: name.to_owned(),
span: expr.span.clone(), span: expr.span.clone(),
})?, })?,
}) })
@ -229,52 +191,52 @@ impl ExecEnv {
StmtKind::Print(expr) => { StmtKind::Print(expr) => {
println!("{}", self.eval_expr(expr)?); println!("{}", self.eval_expr(expr)?);
} }
StmtKind::Var { iden, init } => { StmtKind::Var { ident, init } => {
let init = match init { let init = match init {
Some(e) => self.eval_expr(e)?, Some(e) => self.eval_expr(e)?,
None => Value::Nul, None => Value::Nul,
}; };
self.decl_var(&iden.iden, init); self.decl_var(&ident.ident, init);
} }
StmtKind::Functio { iden, params, body } => { StmtKind::Functio {
ident,
params,
body,
} => {
self.decl_var( self.decl_var(
&iden.iden, &ident.ident,
Value::Functio(Functio::AbleFunctio { Value::Functio(Functio::Able {
params: params.iter().map(|iden| iden.iden.to_string()).collect(), params: params.iter().map(|ident| ident.ident.to_owned()).collect(),
body: body.block.to_owned(), body: body.block.to_owned(),
}), }),
); );
} }
StmtKind::BfFunctio { StmtKind::BfFunctio {
iden, ident,
tape_len, tape_len,
code, code,
} => { } => {
self.decl_var( self.decl_var(
&iden.iden, &ident.ident,
Value::Functio(Functio::BfFunctio { Value::Functio(Functio::Bf {
instructions: code.to_owned(), instructions: code.to_owned(),
tape_len: tape_len tape_len: tape_len
.as_ref() .as_ref()
.map(|tape_len| self.eval_expr(tape_len).map(|v| v.to_i32() as usize)) .map(|tape_len| self.eval_expr(tape_len).map(|v| v.into_i32() as usize))
.unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?, .unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?,
}), }),
); );
} }
StmtKind::If { cond, body } => { StmtKind::If { cond, body } => {
if self.eval_expr(cond)?.to_bool() { if self.eval_expr(cond)?.into_bool() {
return self.eval_stmts_hs(&body.block, true); return self.eval_stmts_hs(&body.block, true);
} }
} }
StmtKind::Call { expr, args } => { StmtKind::Call { expr, args } => {
let func = self.eval_expr(expr)?; let func = self.eval_expr(expr)?.into_functio();
if let Value::Functio(func) = func { self.fn_call(func, args, &stmt.span)?;
self.fn_call(func, args, &stmt.span)?;
} else {
// Fail silently for now.
}
} }
StmtKind::Loop { body } => loop { StmtKind::Loop { body } => loop {
let res = self.eval_stmts_hs(&body.block, true)?; let res = self.eval_stmts_hs(&body.block, true)?;
@ -284,9 +246,8 @@ impl ExecEnv {
HaltStatus::Hopback(_) => continue, HaltStatus::Hopback(_) => continue,
} }
}, },
StmtKind::Assign { iden, value } => { StmtKind::Assign { assignable, value } => {
let value = self.eval_expr(value)?; self.assign(assignable, self.eval_expr(value)?)?;
self.get_var_mut(iden)?.value.replace(value);
} }
StmtKind::Break => { StmtKind::Break => {
return Ok(HaltStatus::Break(stmt.span.clone())); return Ok(HaltStatus::Break(stmt.span.clone()));
@ -294,8 +255,8 @@ impl ExecEnv {
StmtKind::HopBack => { StmtKind::HopBack => {
return Ok(HaltStatus::Hopback(stmt.span.clone())); return Ok(HaltStatus::Hopback(stmt.span.clone()));
} }
StmtKind::Melo(iden) => { StmtKind::Melo(ident) => {
self.get_var_mut(iden)?.melo = true; self.get_var_mut(ident)?.melo = true;
} }
StmtKind::Rlyeh => { StmtKind::Rlyeh => {
// Maybe print a creepy error message or something // Maybe print a creepy error message or something
@ -307,20 +268,69 @@ impl ExecEnv {
.write_all(include_str!("rickroll").as_bytes()) .write_all(include_str!("rickroll").as_bytes())
.expect("Failed to write to stdout"); .expect("Failed to write to stdout");
} }
StmtKind::Read(iden) => { StmtKind::Read(assignable) => {
let mut value = 0; let mut value = 0;
for _ in 0..READ_BITS { for _ in 0..READ_BITS {
value <<= 1; value <<= 1;
value += self.get_bit()? as i32; value += self.get_bit()? as i32;
} }
self.get_var_mut(iden)?.value.replace(Value::Int(value)); self.assign(assignable, Value::Int(value))?;
} }
} }
Ok(HaltStatus::Finished) Ok(HaltStatus::Finished)
} }
/// Assign a value to an Assignable.
fn assign(&mut self, dest: &Assignable, value: Value) -> Result<(), Error> {
match dest.kind {
AssignableKind::Variable => {
self.get_var_mut(&dest.ident)?.value.replace(value);
}
AssignableKind::Index { ref indices } => {
let mut cell = self.get_var_rc(&dest.ident)?;
for index in indices {
let index = self.eval_expr(index)?;
let next_cell = match &mut *cell.borrow_mut() {
Value::Cart(c) => {
// cell is a cart, so we can do simple
// indexing.
if let Some(x) = c.get(&index) {
// cell[index] exists, get a shared
// reference to it.
Rc::clone(x)
} else {
// cell[index] does not exist, so we
// insert an empty cart by default
// instead.
let next_cell =
Rc::new(RefCell::new(Value::Cart(Default::default())));
c.insert(index, Rc::clone(&next_cell));
next_cell
}
}
x => {
// cell is not a cart; `take` it, convert
// it into a cart, and write the result
// back into it.
let mut cart = take(x).into_cart();
let next_cell = Rc::new(RefCell::new(Value::Cart(Default::default())));
cart.insert(index, Rc::clone(&next_cell));
*x = Value::Cart(cart);
next_cell
}
};
cell = next_cell;
}
cell.replace(value);
}
}
Ok(())
}
/// Call a function with the given arguments (i.e., actual /// Call a function with the given arguments (i.e., actual
/// parameters). If the function invocation fails for some reason, /// parameters). If the function invocation fails for some reason,
/// report the error at `span`. /// report the error at `span`.
@ -331,8 +341,8 @@ impl ExecEnv {
.iter() .iter()
.map(|arg| { .map(|arg| {
if let ExprKind::Variable(name) = &arg.kind { if let ExprKind::Variable(name) = &arg.kind {
self.get_var_rc(&Iden { self.get_var_rc(&Ident {
iden: name.to_owned(), ident: name.to_owned(),
span: arg.span.clone(), span: arg.span.clone(),
}) })
} else { } else {
@ -341,8 +351,17 @@ impl ExecEnv {
}) })
.collect::<Result<Vec<_>, Error>>()?; .collect::<Result<Vec<_>, Error>>()?;
self.fn_call_with_values(func, &args, span)
}
fn fn_call_with_values(
&mut self,
func: Functio,
args: &[Rc<RefCell<Value>>],
span: &Range<usize>,
) -> Result<(), Error> {
match func { match func {
Functio::BfFunctio { Functio::Bf {
instructions, instructions,
tape_len, tape_len,
} => { } => {
@ -368,7 +387,7 @@ impl ExecEnv {
.write_all(&output) .write_all(&output)
.expect("Failed to write to stdout"); .expect("Failed to write to stdout");
} }
Functio::AbleFunctio { params, body } => { Functio::Able { params, body } => {
if params.len() != args.len() { if params.len() != args.len() {
return Err(Error { return Err(Error {
kind: ErrorKind::MismatchedArgumentError, kind: ErrorKind::MismatchedArgumentError,
@ -387,10 +406,74 @@ impl ExecEnv {
self.stack.pop(); self.stack.pop();
res?; res?;
} }
Functio::Chain { functios, kind } => {
let (left_functio, right_functio) = *functios;
match kind {
crate::variables::FunctioChainKind::Equal => {
let (l, r) = args.split_at(args.len() / 2);
self.fn_call_with_values(left_functio, l, span)?;
self.fn_call_with_values(right_functio, r, span)?;
}
crate::variables::FunctioChainKind::ByArity => {
let (l, r) =
Self::deinterlace(args, (left_functio.arity(), right_functio.arity()));
self.fn_call_with_values(left_functio, &l, span)?;
self.fn_call_with_values(right_functio, &r, span)?;
}
};
}
Functio::Eval(code) => {
if !args.is_empty() {
return Err(Error {
kind: ErrorKind::MismatchedArgumentError,
span: span.to_owned(),
});
}
let stmts = crate::parser::Parser::new(&code).init()?;
self.eval_stmts(&stmts)?;
}
} }
Ok(()) Ok(())
} }
fn deinterlace(
args: &[Rc<RefCell<Value>>],
arities: (usize, usize),
) -> (Vec<Rc<RefCell<Value>>>, Vec<Rc<RefCell<Value>>>) {
let n_alternations = usize::min(arities.0, arities.1);
let (extra_l, extra_r) = match Ord::cmp(&arities.0, &arities.1) {
Ordering::Less => (0, arities.1 - arities.0),
Ordering::Equal => (0, 0),
Ordering::Greater => (arities.0 - arities.1, 0),
};
(
args.chunks(2)
.take(n_alternations)
.map(|chunk| Rc::clone(&chunk[0]))
.chain(
args[2 * n_alternations..]
.iter()
.map(|r| Rc::clone(r))
.take(extra_l),
)
.collect(),
args.chunks(2)
.take(n_alternations)
.map(|chunk| Rc::clone(&chunk[1]))
.chain(
args[2 * n_alternations..]
.iter()
.map(|r| Rc::clone(r))
.take(extra_r),
)
.collect(),
)
}
/// Get a single bit from the bit buffer, or refill it from /// Get a single bit from the bit buffer, or refill it from
/// standard input if it is empty. /// standard input if it is empty.
fn get_bit(&mut self) -> Result<bool, Error> { fn get_bit(&mut self) -> Result<bool, Error> {
@ -413,9 +496,9 @@ impl ExecEnv {
/// Get the value of a variable. Throw an error if the variable is /// Get the value of a variable. Throw an error if the variable is
/// inaccessible or banned. /// inaccessible or banned.
fn get_var(&self, name: &Iden) -> Result<Value, Error> { fn get_var(&self, name: &Ident) -> Result<Value, Error> {
// One-letter names are reserved as base55 numbers. // One-letter names are reserved as base55 numbers.
let mut chars = name.iden.chars(); let mut chars = name.ident.chars();
if let (Some(first), None) = (chars.next(), chars.next()) { if let (Some(first), None) = (chars.next(), chars.next()) {
return Ok(Value::Int(base_55::char2num(first))); return Ok(Value::Int(base_55::char2num(first)));
} }
@ -426,20 +509,20 @@ impl ExecEnv {
.stack .stack
.iter() .iter()
.rev() .rev()
.find_map(|scope| scope.variables.get(&name.iden)) .find_map(|scope| scope.variables.get(&name.ident))
{ {
Some(var) => { Some(var) => {
if !var.melo { if !var.melo {
Ok(var.value.borrow().clone()) Ok(var.value.borrow().clone())
} else { } else {
Err(Error { Err(Error {
kind: ErrorKind::MeloVariable(name.iden.to_owned()), kind: ErrorKind::MeloVariable(name.ident.to_owned()),
span: name.span.clone(), span: name.span.clone(),
}) })
} }
} }
None => Err(Error { None => Err(Error {
kind: ErrorKind::UnknownVariable(name.iden.to_owned()), kind: ErrorKind::UnknownVariable(name.ident.to_owned()),
span: name.span.clone(), span: name.span.clone(),
}), }),
} }
@ -447,27 +530,27 @@ impl ExecEnv {
/// Get a mutable reference to a variable. Throw an error if the /// Get a mutable reference to a variable. Throw an error if the
/// variable is inaccessible or banned. /// variable is inaccessible or banned.
fn get_var_mut(&mut self, name: &Iden) -> Result<&mut Variable, Error> { fn get_var_mut(&mut self, name: &Ident) -> Result<&mut Variable, Error> {
// This function has a lot of duplicated code with `get_var`, // This function has a lot of duplicated code with `get_var`,
// which I feel like is a bad sign... // which I feel like is a bad sign...
match self match self
.stack .stack
.iter_mut() .iter_mut()
.rev() .rev()
.find_map(|scope| scope.variables.get_mut(&name.iden)) .find_map(|scope| scope.variables.get_mut(&name.ident))
{ {
Some(var) => { Some(var) => {
if !var.melo { if !var.melo {
Ok(var) Ok(var)
} else { } else {
Err(Error { Err(Error {
kind: ErrorKind::MeloVariable(name.iden.to_owned()), kind: ErrorKind::MeloVariable(name.ident.to_owned()),
span: name.span.clone(), span: name.span.clone(),
}) })
} }
} }
None => Err(Error { None => Err(Error {
kind: ErrorKind::UnknownVariable(name.iden.to_owned()), kind: ErrorKind::UnknownVariable(name.ident.to_owned()),
span: name.span.clone(), span: name.span.clone(),
}), }),
} }
@ -475,7 +558,7 @@ impl ExecEnv {
/// Get an Rc'd pointer to the value of a variable. Throw an error /// Get an Rc'd pointer to the value of a variable. Throw an error
/// if the variable is inaccessible or banned. /// if the variable is inaccessible or banned.
fn get_var_rc(&mut self, name: &Iden) -> Result<Rc<RefCell<Value>>, Error> { fn get_var_rc(&mut self, name: &Ident) -> Result<Rc<RefCell<Value>>, Error> {
Ok(self.get_var_mut(name)?.value.clone()) Ok(self.get_var_mut(name)?.value.clone())
} }
@ -571,7 +654,7 @@ mod tests {
span: 1..1 span: 1..1
}) })
.unwrap(), .unwrap(),
Value::Int(42) Value::Int(-2147483648)
); );
// And the same for divide by zero. // And the same for divide by zero.
@ -579,7 +662,7 @@ mod tests {
env.eval_expr(&Expr { env.eval_expr(&Expr {
kind: ExprKind::BinOp { kind: ExprKind::BinOp {
lhs: Box::new(Expr { lhs: Box::new(Expr {
kind: ExprKind::Literal(Value::Int(1)), kind: ExprKind::Literal(Value::Int(84)),
span: 1..1, span: 1..1,
}), }),
rhs: Box::new(Expr { rhs: Box::new(Expr {
@ -591,7 +674,7 @@ mod tests {
span: 1..1 span: 1..1
}) })
.unwrap(), .unwrap(),
Value::Int(42) Value::Int(2)
); );
} }
@ -617,8 +700,8 @@ mod tests {
// Declaring and reading from a variable. // Declaring and reading from a variable.
eval(&mut env, "var foo = 32; var bar = foo + 1;").unwrap(); eval(&mut env, "var foo = 32; var bar = foo + 1;").unwrap();
assert_eq!( assert_eq!(
env.get_var(&Iden { env.get_var(&Ident {
iden: "bar".to_owned(), ident: "bar".to_owned(),
span: 1..1, span: 1..1,
}) })
.unwrap(), .unwrap(),
@ -628,8 +711,8 @@ mod tests {
// Assigning an existing variable. // Assigning an existing variable.
eval(&mut env, "foo = \"hi\";").unwrap(); eval(&mut env, "foo = \"hi\";").unwrap();
assert_eq!( assert_eq!(
env.get_var(&Iden { env.get_var(&Ident {
iden: "foo".to_owned(), ident: "foo".to_owned(),
span: 1..1, span: 1..1,
}) })
.unwrap(), .unwrap(),
@ -654,8 +737,8 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
env.get_var(&Iden { env.get_var(&Ident {
iden: "foo".to_owned(), ident: "foo".to_owned(),
span: 1..1, span: 1..1,
}) })
.unwrap(), .unwrap(),

View file

@ -64,13 +64,7 @@ pub enum Token {
#[token("!=")] #[token("!=")]
NotEqual, NotEqual,
#[token("&")] #[regex("!|aint")] // also add aint as a not keyword
And,
#[token("|")]
Or,
#[token("!|aint")] // also add aint as a not keyword
Not, Not,
// Keywords // Keywords
@ -123,7 +117,7 @@ pub enum Token {
// Literals // Literals
/// True, False /// True, False
#[regex("true|false", get_bool)] #[regex("true|false", get_value)]
Bool(bool), Bool(bool),
/// Always, Sometimes, Never /// Always, Sometimes, Never
@ -139,11 +133,11 @@ pub enum Token {
String(String), String(String),
/// Integer /// Integer
#[regex(r"-?[0-9]+", get_int)] #[regex(r"-?[0-9]+", get_value)]
Integer(i32), Integer(i32),
/// A C-complaint identifier /// A C-complaint identifier
#[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)] #[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_ident)]
Identifier(String), Identifier(String),
#[regex(r"owo .*")] #[regex(r"owo .*")]
@ -157,11 +151,7 @@ pub enum Token {
Error, Error,
} }
fn get_bool(lexer: &mut Lexer<Token>) -> Option<bool> { fn get_value<T: std::str::FromStr>(lexer: &mut Lexer<Token>) -> Option<T> {
lexer.slice().parse().ok()
}
fn get_int(lexer: &mut Lexer<Token>) -> Option<i32> {
lexer.slice().parse().ok() lexer.slice().parse().ok()
} }
@ -178,7 +168,7 @@ fn get_abool(lexer: &mut Lexer<Token>) -> Option<Abool> {
} }
} }
fn get_iden(lexer: &mut Lexer<Token>) -> String { fn get_ident(lexer: &mut Lexer<Token>) -> String {
lexer.slice().to_owned() lexer.slice().to_owned()
} }

View file

@ -37,12 +37,7 @@ impl<'source> Parser<'source> {
Token::Comment => continue, Token::Comment => continue,
// T-Dark block (replace `lang` with `script`) // T-Dark block (replace `lang` with `script`)
Token::TDark => { Token::TDark => ast.extend(self.tdark_flow()?.block),
self.tdark = true;
let mut block = self.get_block()?;
ast.append(&mut block.block);
self.tdark = false;
}
token => ast.push(self.parse(token)?), token => ast.push(self.parse(token)?),
} }
} }
@ -53,9 +48,16 @@ impl<'source> Parser<'source> {
/// ///
/// If EOF, return Error instead of None /// If EOF, return Error instead of None
fn checked_next(&mut self) -> Result<Token, Error> { fn checked_next(&mut self) -> Result<Token, Error> {
self.lexer loop {
.next() match self
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start)) .lexer
.next()
.ok_or_else(|| Error::unexpected_eof(self.lexer.span().start))?
{
Token::Comment => (),
token => break Ok(token),
}
}
} }
/// Parse a token /// Parse a token
@ -76,19 +78,19 @@ impl<'source> Parser<'source> {
Token::Melo => Ok(Stmt::new(self.melo_flow()?, start..self.lexer.span().end)), Token::Melo => Ok(Stmt::new(self.melo_flow()?, start..self.lexer.span().end)),
Token::Loop => Ok(Stmt::new(self.loop_flow()?, start..self.lexer.span().end)), Token::Loop => Ok(Stmt::new(self.loop_flow()?, start..self.lexer.span().end)),
Token::Break => Ok(Stmt::new( Token::Break => Ok(Stmt::new(
self.semi_terminated(StmtKind::Break)?, self.semicolon_terminated(StmtKind::Break)?,
start..self.lexer.span().end, start..self.lexer.span().end,
)), )),
Token::HopBack => Ok(Stmt::new( Token::HopBack => Ok(Stmt::new(
self.semi_terminated(StmtKind::HopBack)?, self.semicolon_terminated(StmtKind::HopBack)?,
start..self.lexer.span().end, start..self.lexer.span().end,
)), )),
Token::Rlyeh => Ok(Stmt::new( Token::Rlyeh => Ok(Stmt::new(
self.semi_terminated(StmtKind::Rlyeh)?, self.semicolon_terminated(StmtKind::Rlyeh)?,
start..self.lexer.span().end, start..self.lexer.span().end,
)), )),
Token::Rickroll => Ok(Stmt::new( Token::Rickroll => Ok(Stmt::new(
self.semi_terminated(StmtKind::Rickroll)?, self.semicolon_terminated(StmtKind::Rickroll)?,
start..self.lexer.span().end, start..self.lexer.span().end,
)), )),
@ -98,6 +100,8 @@ impl<'source> Parser<'source> {
| Token::Integer(_) | Token::Integer(_)
| Token::Abool(_) | Token::Abool(_)
| Token::Bool(_) | Token::Bool(_)
| Token::Nul
| Token::Not
| Token::LeftBracket | Token::LeftBracket
| Token::LeftParen => Ok(Stmt::new( | Token::LeftParen => Ok(Stmt::new(
self.value_flow(token)?, self.value_flow(token)?,
@ -114,7 +118,7 @@ impl<'source> Parser<'source> {
/// Require statement to be semicolon terminated /// Require statement to be semicolon terminated
/// ///
/// Utility function for short statements /// Utility function for short statements
fn semi_terminated(&mut self, stmt_kind: StmtKind) -> Result<StmtKind, Error> { fn semicolon_terminated(&mut self, stmt_kind: StmtKind) -> Result<StmtKind, Error> {
self.require(Token::Semicolon)?; self.require(Token::Semicolon)?;
Ok(stmt_kind) Ok(stmt_kind)
} }
@ -129,13 +133,13 @@ impl<'source> Parser<'source> {
} }
/// Get an Identifier /// Get an Identifier
fn get_iden(&mut self) -> Result<Iden, Error> { fn get_ident(&mut self) -> Result<Ident, Error> {
match self.checked_next()? { match self.checked_next()? {
Token::Identifier(iden) => Ok(Iden { Token::Identifier(ident) => Ok(Ident {
iden: if self.tdark { ident: if self.tdark {
iden.replace("lang", "script") ident.replace("lang", "script")
} else { } else {
iden ident
}, },
span: self.lexer.span(), span: self.lexer.span(),
}), }),
@ -192,7 +196,7 @@ impl<'source> Parser<'source> {
Token::LeftBracket => match buf.take() { Token::LeftBracket => match buf.take() {
Some(buf) => Ok(Expr::new( Some(buf) => Ok(Expr::new(
ExprKind::Index { ExprKind::Index {
cart: Box::new(buf), expr: Box::new(buf),
index: Box::new(self.expr_flow(Token::RightBracket)?), index: Box::new(self.expr_flow(Token::RightBracket)?),
}, },
start..self.lexer.span().end, start..self.lexer.span().end,
@ -208,9 +212,7 @@ impl<'source> Parser<'source> {
| Token::EqualEqual | Token::EqualEqual
| Token::NotEqual | Token::NotEqual
| Token::LessThan | Token::LessThan
| Token::GreaterThan | Token::GreaterThan => Ok(Expr::new(
| Token::And
| Token::Or => Ok(Expr::new(
self.binop_flow( self.binop_flow(
BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?, BinOpKind::from_token(token).map_err(|e| Error::new(e, self.lexer.span()))?,
buf, buf,
@ -322,12 +324,21 @@ impl<'source> Parser<'source> {
loop { loop {
match self.checked_next()? { match self.checked_next()? {
Token::RightCurly => break, Token::RightCurly => break,
Token::TDark => block.extend(self.tdark_flow()?.block),
t => block.push(self.parse(t)?), t => block.push(self.parse(t)?),
} }
} }
Ok(Block { block }) Ok(Block { block })
} }
/// Parse T-Dark block
fn tdark_flow(&mut self) -> Result<Block, Error> {
self.tdark = true;
let block = self.get_block();
self.tdark = false;
block
}
/// If Statement parser gets any kind of value (Identifier or Literal) /// If Statement parser gets any kind of value (Identifier or Literal)
/// It cannot parse it as it do not parse expressions. Instead of it it /// It cannot parse it as it do not parse expressions. Instead of it it
/// will parse it to function call or print statement. /// will parse it to function call or print statement.
@ -340,7 +351,7 @@ impl<'source> Parser<'source> {
let stmt = StmtKind::Print(buf.take().ok_or_else(|| { let stmt = StmtKind::Print(buf.take().ok_or_else(|| {
Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span()) Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span())
})?); })?);
break self.semi_terminated(stmt)?; break self.semicolon_terminated(stmt)?;
} }
// Functio call // Functio call
@ -355,26 +366,29 @@ impl<'source> Parser<'source> {
// Variable Assignment // Variable Assignment
Token::Equal => { Token::Equal => {
if let Some(Expr { if let Some(Ok(assignable)) = buf.take().map(Assignable::from_expr) {
kind: ExprKind::Variable(iden),
span,
}) = buf
{
break StmtKind::Assign { break StmtKind::Assign {
iden: Iden::new(iden, span), assignable,
value: self.expr_flow(Token::Semicolon)?, value: self.expr_flow(Token::Semicolon)?,
}; };
} else {
return Err(Error::new(
ErrorKind::UnexpectedToken(Token::Equal),
self.lexer.span(),
));
} }
} }
// Read input // Read input
Token::Read => { Token::Read => {
if let Some(Expr { if let Some(Ok(assignable)) = buf.take().map(Assignable::from_expr) {
kind: ExprKind::Variable(iden), self.require(Token::Semicolon)?;
span, break StmtKind::Read(assignable);
}) = buf } else {
{ return Err(Error::new(
break self.semi_terminated(StmtKind::Read(Iden::new(iden, span)))?; ErrorKind::UnexpectedToken(Token::Read),
self.lexer.span(),
));
} }
} }
@ -400,9 +414,9 @@ impl<'source> Parser<'source> {
/// Parse functio flow /// Parse functio flow
/// ///
/// functio $iden (a, b, c) { ... } /// functio $ident (a, b, c) { ... }
fn functio_flow(&mut self) -> Result<StmtKind, Error> { fn functio_flow(&mut self) -> Result<StmtKind, Error> {
let iden = self.get_iden()?; let ident = self.get_ident()?;
self.require(Token::LeftParen)?; self.require(Token::LeftParen)?;
@ -411,7 +425,7 @@ impl<'source> Parser<'source> {
match self.checked_next()? { match self.checked_next()? {
Token::RightParen => break, Token::RightParen => break,
Token::Identifier(i) => { Token::Identifier(i) => {
params.push(Iden::new(i, self.lexer.span())); params.push(Ident::new(i, self.lexer.span()));
// Require comma (next) or right paren (end) after identifier // Require comma (next) or right paren (end) after identifier
match self.checked_next()? { match self.checked_next()? {
@ -431,14 +445,18 @@ impl<'source> Parser<'source> {
let body = self.get_block()?; let body = self.get_block()?;
Ok(StmtKind::Functio { iden, params, body }) Ok(StmtKind::Functio {
ident,
params,
body,
})
} }
/// Parse BF function declaration /// Parse BF function declaration
/// ///
/// `bff $iden ([tapelen]) { ... }` /// `bff $ident ([tapelen]) { ... }`
fn bff_flow(&mut self) -> Result<StmtKind, Error> { fn bff_flow(&mut self) -> Result<StmtKind, Error> {
let iden = self.get_iden()?; let ident = self.get_ident()?;
let tape_len = match self.checked_next()? { let tape_len = match self.checked_next()? {
Token::LeftParen => { Token::LeftParen => {
@ -472,7 +490,7 @@ impl<'source> Parser<'source> {
} }
Ok(StmtKind::BfFunctio { Ok(StmtKind::BfFunctio {
iden, ident,
tape_len, tape_len,
code, code,
}) })
@ -513,20 +531,20 @@ impl<'source> Parser<'source> {
/// Parse variable declaration /// Parse variable declaration
fn var_flow(&mut self) -> Result<StmtKind, Error> { fn var_flow(&mut self) -> Result<StmtKind, Error> {
let iden = self.get_iden()?; let ident = self.get_ident()?;
let init = match self.checked_next()? { let init = match self.checked_next()? {
Token::Equal => Some(self.expr_flow(Token::Semicolon)?), Token::Equal => Some(self.expr_flow(Token::Semicolon)?),
Token::Semicolon => None, Token::Semicolon => None,
t => return Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())), t => return Err(Error::new(ErrorKind::UnexpectedToken(t), self.lexer.span())),
}; };
Ok(StmtKind::Var { iden, init }) Ok(StmtKind::Var { ident, init })
} }
/// Parse Melo flow /// Parse Melo flow
fn melo_flow(&mut self) -> Result<StmtKind, Error> { fn melo_flow(&mut self) -> Result<StmtKind, Error> {
let iden = self.get_iden()?; let ident = self.get_ident()?;
self.semi_terminated(StmtKind::Melo(iden)) self.semicolon_terminated(StmtKind::Melo(ident))
} }
/// Parse loop flow /// Parse loop flow
@ -558,7 +576,7 @@ mod tests {
rhs: Box::new(Expr { rhs: Box::new(Expr {
kind: ExprKind::BinOp { kind: ExprKind::BinOp {
lhs: Box::new(Expr { lhs: Box::new(Expr {
kind: ExprKind::Variable("a".to_string()), kind: ExprKind::Variable("a".to_owned()),
span: 5..6, span: 5..6,
}), }),
rhs: Box::new(Expr { rhs: Box::new(Expr {
@ -593,8 +611,8 @@ mod tests {
let code = r#"var a = 42;"#; let code = r#"var a = 42;"#;
let expected = &[Stmt { let expected = &[Stmt {
kind: StmtKind::Var { kind: StmtKind::Var {
iden: Iden { ident: Ident {
iden: "a".to_string(), ident: "a".to_owned(),
span: 4..5, span: 4..5,
}, },
init: Some(Expr { init: Some(Expr {
@ -631,7 +649,7 @@ mod tests {
body: Block { body: Block {
block: vec![Stmt { block: vec![Stmt {
kind: StmtKind::Print(Expr { kind: StmtKind::Print(Expr {
kind: ExprKind::Literal(Value::Str("Buy Able products!".to_string())), kind: ExprKind::Literal(Value::Str("Buy Able products!".to_owned())),
span: 19..39, span: 19..39,
}), }),
span: 19..46, span: 19..46,
@ -650,18 +668,18 @@ mod tests {
let code = r#"T-Dark { var lang = "lang" + lang; }"#; let code = r#"T-Dark { var lang = "lang" + lang; }"#;
let expected = &[Stmt { let expected = &[Stmt {
kind: StmtKind::Var { kind: StmtKind::Var {
iden: Iden { ident: Ident {
iden: "script".to_string(), ident: "script".to_owned(),
span: 13..17, span: 13..17,
}, },
init: Some(Expr { init: Some(Expr {
kind: ExprKind::BinOp { kind: ExprKind::BinOp {
lhs: Box::new(Expr { lhs: Box::new(Expr {
kind: ExprKind::Literal(Value::Str("script".to_string())), kind: ExprKind::Literal(Value::Str("script".to_owned())),
span: 20..26, span: 20..26,
}), }),
rhs: Box::new(Expr { rhs: Box::new(Expr {
kind: ExprKind::Variable("script".to_string()), kind: ExprKind::Variable("script".to_owned()),
span: 29..33, span: 29..33,
}), }),
kind: BinOpKind::Add, kind: BinOpKind::Add,
@ -684,7 +702,7 @@ mod tests {
kind: ExprKind::Cart(vec![ kind: ExprKind::Cart(vec![
( (
Expr { Expr {
kind: ExprKind::Literal(Value::Str("able".to_string())), kind: ExprKind::Literal(Value::Str("able".to_owned())),
span: 1..7, span: 1..7,
}, },
Expr { Expr {
@ -694,7 +712,7 @@ mod tests {
), ),
( (
Expr { Expr {
kind: ExprKind::Literal(Value::Str("script".to_string())), kind: ExprKind::Literal(Value::Str("script".to_owned())),
span: 14..22, span: 14..22,
}, },
Expr { Expr {
@ -728,14 +746,14 @@ mod tests {
let expected = &[Stmt { let expected = &[Stmt {
kind: StmtKind::Print(Expr { kind: StmtKind::Print(Expr {
kind: ExprKind::Index { kind: ExprKind::Index {
cart: Box::new(Expr { expr: Box::new(Expr {
kind: ExprKind::Cart(vec![( kind: ExprKind::Cart(vec![(
Expr { Expr {
kind: ExprKind::Literal(Value::Str("able".to_string())), kind: ExprKind::Literal(Value::Str("able".to_owned())),
span: 1..7, span: 1..7,
}, },
Expr { Expr {
kind: ExprKind::Literal(Value::Str("ablecorp".to_string())), kind: ExprKind::Literal(Value::Str("ablecorp".to_owned())),
span: 11..21, span: 11..21,
}, },
)]), )]),

View file

@ -1,13 +1,13 @@
use std::{ use std::{
cell::RefCell, collections::HashMap, convert::TryFrom, fmt::Display, hash::Hash, io::Write, cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, io::Write, mem::discriminant,
mem::discriminant, rc::Rc, ops, rc::Rc, vec,
}; };
use rand::Rng; use rand::Rng;
use crate::{ast::Stmt, consts}; use crate::{ast::Stmt, brian::INSTRUCTION_MAPPINGS, consts};
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Abool { pub enum Abool {
Never = -1, Never = -1,
Sometimes = 0, Sometimes = 0,
@ -36,16 +36,43 @@ impl From<Abool> for bool {
#[derive(Debug, PartialEq, Clone, Hash)] #[derive(Debug, PartialEq, Clone, Hash)]
pub enum Functio { pub enum Functio {
BfFunctio { Bf {
instructions: Vec<u8>, instructions: Vec<u8>,
tape_len: usize, tape_len: usize,
}, },
AbleFunctio { Able {
params: Vec<String>, params: Vec<String>,
body: Vec<Stmt>, body: Vec<Stmt>,
}, },
Chain {
functios: Box<(Functio, Functio)>,
kind: FunctioChainKind,
},
Eval(String),
} }
impl Functio {
pub fn arity(&self) -> usize {
match self {
Functio::Bf {
instructions: _,
tape_len: _,
} => 0,
Functio::Able { params, body: _ } => params.len(),
Functio::Chain { functios, kind: _ } => functios.0.arity() + functios.1.arity(),
Functio::Eval(_) => 0,
}
}
}
#[derive(Debug, PartialEq, Copy, Clone, Hash)]
pub enum FunctioChainKind {
Equal,
ByArity,
}
pub type Cart = HashMap<Value, Rc<RefCell<Value>>>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Value { pub enum Value {
Nul, Nul,
@ -54,7 +81,13 @@ pub enum Value {
Bool(bool), Bool(bool),
Abool(Abool), Abool(Abool),
Functio(Functio), Functio(Functio),
Cart(HashMap<Value, Rc<RefCell<Value>>>), Cart(Cart),
}
impl Default for Value {
fn default() -> Self {
Self::Nul
}
} }
impl Hash for Value { impl Hash for Value {
@ -72,24 +105,6 @@ impl Hash for Value {
} }
} }
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Nul, Value::Nul) => true,
(Value::Str(left), Value::Str(right)) => left == right,
(Value::Int(left), Value::Int(right)) => left == right,
(Value::Bool(left), Value::Bool(right)) => left == right,
(Value::Abool(left), Value::Abool(right)) => left == right,
(Value::Functio(left), Value::Functio(right)) => left == right,
(Value::Cart(_), Value::Cart(_)) => self.to_string() == other.to_string(),
(_, _) => false,
// TODO: do more coercions!
}
}
}
impl Eq for Value {}
impl Value { impl Value {
/// Write an AbleScript value to a Brainfuck input stream by /// Write an AbleScript value to a Brainfuck input stream by
/// coercing the value to an integer, then truncating that integer /// coercing the value to an integer, then truncating that integer
@ -98,73 +113,668 @@ impl Value {
/// any IO errors will cause a panic. /// any IO errors will cause a panic.
pub fn bf_write(&self, stream: &mut impl Write) { pub fn bf_write(&self, stream: &mut impl Write) {
stream stream
.write_all(&[self.clone().to_i32() as u8]) .write_all(&[self.clone().into_i32() as u8])
.expect("Failed to write to Brainfuck input"); .expect("Failed to write to Brainfuck input");
} }
/// Coerce a value to an integer. /// Coerce a value to an integer.
pub fn to_i32(&self) -> i32 { pub fn into_i32(self) -> i32 {
match self { match self {
Value::Abool(a) => *a as _, Value::Abool(a) => a as _,
Value::Bool(b) => *b as _, Value::Bool(b) => b as _,
Value::Functio(func) => match func { Value::Functio(func) => match func {
Functio::BfFunctio { // Compares lengths of functions:
// BfFunctio - Sum of lengths of instructions and length of tape
// AbleFunctio - Sum of argument count and body length
// Eval - Length of input code
Functio::Bf {
instructions, instructions,
tape_len, tape_len,
} => (instructions.len() + tape_len) as _, } => (instructions.len() + tape_len) as _,
Functio::AbleFunctio { params, body } => (params.len() + body.len()) as _, Functio::Able { params, body } => (params.len() + format!("{:?}", body).len()) as _,
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios;
match kind {
FunctioChainKind::Equal => {
Value::Int(Value::Functio(lhs).into_i32())
+ Value::Int(Value::Functio(rhs).into_i32())
}
FunctioChainKind::ByArity => {
Value::Int(Value::Functio(lhs).into_i32())
* Value::Int(Value::Functio(rhs).into_i32())
}
}
.into_i32()
}
Functio::Eval(s) => s.len() as _,
}, },
Value::Int(i) => *i, Value::Int(i) => i,
Value::Nul => consts::ANSWER, Value::Nul => consts::ANSWER,
Value::Str(text) => text.parse().unwrap_or(consts::ANSWER), Value::Str(text) => text.parse().unwrap_or(consts::ANSWER),
Value::Cart(c) => c.len() as _, Value::Cart(c) => c.len() as _,
} }
} }
/// Coerce a Value to a boolean. The conversion cannot fail. /// Coerce a value to a boolean.
pub fn to_bool(&self) -> bool { pub fn into_bool(self) -> bool {
match self { match self {
Value::Abool(b) => (*b).into(), Value::Abool(b) => b.into(),
Value::Bool(b) => *b, Value::Bool(b) => b,
Value::Functio(_) => true, Value::Functio(_) => true,
Value::Int(x) => *x != 0, Value::Int(x) => x != 0,
Value::Nul => true, Value::Nul => false,
Value::Str(s) => !s.is_empty(), Value::Str(s) => match s.to_lowercase().as_str() {
"false" | "no" | "🇳🇴" => false,
"true" | "yes" => true,
s => !s.is_empty(),
},
Value::Cart(c) => !c.is_empty(), Value::Cart(c) => !c.is_empty(),
} }
} }
/// Index a value with another value, as in the "a[b]" syntax. /// Coerce a value to an aboolean.
pub fn index(&self, index: &Value) -> Rc<RefCell<Value>> { pub fn into_abool(self) -> Abool {
Rc::new(RefCell::new(match self { match self {
Value::Nul => Value::Nul, Value::Nul => Abool::Never,
Value::Str(s) => Value::Int( Value::Str(s) => match s.to_lowercase().as_str() {
usize::try_from(index.to_i32() - 1) "never" => Abool::Never,
.ok() "sometimes" => Abool::Sometimes,
.and_then(|idx| s.as_bytes().get(idx).cloned()) "always" => Abool::Always,
.map(|value| value as i32) s => {
.unwrap_or(0), if s.is_empty() {
), Abool::Never
Value::Int(i) => Value::Int( } else {
usize::try_from(index.to_i32() - 1) Abool::Always
.ok() }
.and_then(|idx| format!("{}", i).as_bytes().get(idx).cloned()) }
.map(|value| value as i32) },
.unwrap_or(0), Value::Int(x) => match x.cmp(&0) {
), std::cmp::Ordering::Less => Abool::Never,
Value::Bool(b) => Value::Int( std::cmp::Ordering::Equal => Abool::Sometimes,
usize::try_from(index.to_i32() - 1) std::cmp::Ordering::Greater => Abool::Always,
.ok() },
.and_then(|idx| format!("{}", b).as_bytes().get(idx).cloned()) Value::Bool(b) => {
.map(|value| value as i32) if b {
.unwrap_or(0), Abool::Always
), } else {
Value::Abool(b) => Value::Int(*b as i32), Abool::Never
Value::Functio(_) => Value::Int(42), }
Value::Cart(c) => {
return (c.get(index).cloned()).unwrap_or_else(|| Rc::new(RefCell::new(Value::Nul)))
} }
})) Value::Abool(a) => a,
Value::Functio(f) => match f {
Functio::Bf {
instructions,
tape_len,
} => Value::Int(
(instructions.iter().map(|x| *x as usize).sum::<usize>() * tape_len) as _,
)
.into_abool(),
Functio::Able { params, body } => {
let str_to_i32 =
|x: String| -> i32 { x.as_bytes().into_iter().map(|x| *x as i32).sum() };
let params: i32 = params.into_iter().map(str_to_i32).sum();
let body: i32 = body
.into_iter()
.map(|x| format!("{:?}", x))
.map(str_to_i32)
.sum();
Value::Int((params + body) % 3 - 1).into_abool()
}
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios;
match kind {
FunctioChainKind::Equal => {
Value::Abool(Value::Functio(lhs).into_abool())
+ Value::Abool(Value::Functio(rhs).into_abool())
}
FunctioChainKind::ByArity => {
Value::Abool(Value::Functio(lhs).into_abool())
* Value::Abool(Value::Functio(rhs).into_abool())
}
}
.into_abool()
}
Functio::Eval(code) => Value::Str(code).into_abool(),
},
Value::Cart(c) => {
if c.is_empty() {
Abool::Never
} else {
Abool::Always
}
}
}
}
/// Coerce a value to a functio.
pub fn into_functio(self) -> Functio {
match self {
Value::Nul => Functio::Able {
body: vec![],
params: vec![],
},
Value::Str(s) => Functio::Eval(s),
Value::Int(i) => Functio::Bf {
instructions: {
std::iter::successors(Some(i as usize), |i| {
Some(i / INSTRUCTION_MAPPINGS.len())
})
.take_while(|&i| i != 0)
.map(|i| INSTRUCTION_MAPPINGS[i % INSTRUCTION_MAPPINGS.len()])
.collect()
},
tape_len: crate::brian::DEFAULT_TAPE_SIZE_LIMIT,
},
Value::Bool(b) => Functio::Eval(
if b {
r#"loop{"Buy Able products!"print;}"#
} else {
""
}
.to_owned(),
),
Value::Abool(a) => Functio::Eval(match a {
Abool::Never => "".to_owned(),
Abool::Sometimes => {
use rand::seq::SliceRandom;
let mut str_chars: Vec<_> = "Buy Able Products!".chars().collect();
str_chars.shuffle(&mut rand::thread_rng());
format!(r#""{}"print;"#, str_chars.iter().collect::<String>())
}
Abool::Always => r#"loop{"Buy Able products!"print;}"#.to_owned(),
}),
Value::Functio(f) => f,
Value::Cart(c) => {
let kind = if let Some(114514) = c
.get(&Value::Str("1452251871514141792252515212116".to_owned()))
.map(|x| x.borrow().to_owned().into_i32())
{
FunctioChainKind::Equal
} else {
FunctioChainKind::ByArity
};
let mut cart_vec = c.iter().collect::<Vec<_>>();
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
cart_vec
.into_iter()
.map(|(_, x)| x.borrow().to_owned().into_functio())
.reduce(|acc, x| Functio::Chain {
functios: Box::new((acc, x)),
kind,
})
.unwrap_or_else(|| Functio::Eval(r#""Buy Able Products!"print;"#.to_owned()))
}
}
}
/// Coerce a value into a cart.
pub fn into_cart(self) -> Cart {
match self {
Value::Nul => HashMap::new(),
Value::Str(s) => s
.chars()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as i32 + 1),
Rc::new(RefCell::new(Value::Str(x.to_string()))),
)
})
.collect(),
Value::Int(i) => Value::Str(i.to_string()).into_cart(),
Value::Bool(b) => Value::Str(b.to_string()).into_cart(),
Value::Abool(a) => Value::Str(a.to_string()).into_cart(),
Value::Functio(f) => match f {
Functio::Able { params, body } => {
let params: Cart = params
.into_iter()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as i32 + 1),
Rc::new(RefCell::new(Value::Str(x))),
)
})
.collect();
let body: Cart = body
.into_iter()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as i32 + 1),
Rc::new(RefCell::new(Value::Str(format!("{:?}", x)))),
)
})
.collect();
let mut cart = HashMap::new();
cart.insert(
Value::Str("params".to_owned()),
Rc::new(RefCell::new(Value::Cart(params))),
);
cart.insert(
Value::Str("body".to_owned()),
Rc::new(RefCell::new(Value::Cart(body))),
);
cart
}
Functio::Bf {
instructions,
tape_len,
} => {
let mut cart: Cart = instructions
.into_iter()
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as i32 + 1),
Rc::new(RefCell::new(
char::from_u32(x as u32)
.map(|x| Value::Str(x.to_string()))
.unwrap_or(Value::Nul),
)),
)
})
.collect();
cart.insert(
Value::Str("tapelen".to_owned()),
Rc::new(RefCell::new(Value::Int(tape_len as _))),
);
cart
}
Functio::Chain { functios, kind } => {
let (lhs, rhs) = *functios;
match kind {
FunctioChainKind::Equal => {
Value::Cart(Value::Functio(lhs).into_cart())
+ Value::Cart(Value::Functio(rhs).into_cart())
}
FunctioChainKind::ByArity => {
Value::Cart(Value::Functio(lhs).into_cart())
* Value::Cart(Value::Functio(rhs).into_cart())
}
}
.into_cart()
}
Functio::Eval(s) => Value::Str(s).into_cart(),
},
Value::Cart(c) => c,
}
}
}
impl ops::Add for Value {
type Output = Value;
fn add(self, rhs: Self) -> Self::Output {
match self {
Value::Nul => match rhs {
Value::Nul => Value::Nul,
Value::Str(_) => Value::Str(self.to_string()) + rhs,
Value::Int(_) => Value::Int(self.into_i32()) + rhs,
Value::Bool(_) => Value::Bool(self.into_bool()) + rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) + rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) + rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) + rhs,
},
Value::Str(s) => Value::Str(format!("{}{}", s, rhs.to_string())),
Value::Int(i) => Value::Int(i.wrapping_add(rhs.into_i32())),
Value::Bool(b) => Value::Bool(b || rhs.into_bool()),
Value::Abool(_) => {
Value::Abool(Value::Int(self.into_i32().max(rhs.into_i32())).into_abool())
}
Value::Functio(f) => Value::Functio(Functio::Chain {
functios: Box::new((f, rhs.into_functio())),
kind: FunctioChainKind::Equal,
}),
Value::Cart(c) => {
Value::Cart(c.into_iter().chain(rhs.into_cart().into_iter()).collect())
}
}
}
}
impl ops::Sub for Value {
type Output = Value;
fn sub(self, rhs: Self) -> Self::Output {
match self {
Value::Nul => match rhs {
Value::Nul => Value::Nul,
Value::Str(_) => Value::Str(self.to_string()) - rhs,
Value::Int(_) => Value::Int(self.into_i32()) - rhs,
Value::Bool(_) => Value::Bool(self.into_bool()) - rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) - rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) - rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) - rhs,
},
Value::Str(s) => Value::Str(s.replace(&rhs.to_string(), "")),
Value::Int(i) => Value::Int(i.wrapping_sub(rhs.into_i32())),
Value::Bool(b) => Value::Bool(b ^ rhs.into_bool()),
Value::Abool(_) => (self.clone() + rhs.clone()) * !(self * rhs),
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
instructions: lhs_ins,
tape_len: lhs_tl,
} => match rhs.into_functio() {
Functio::Bf {
instructions: rhs_ins,
tape_len: rhs_tl,
} => Functio::Bf {
instructions: lhs_ins
.into_iter()
.zip(rhs_ins.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
tape_len: lhs_tl - rhs_tl,
},
rhs => Functio::Bf {
instructions: lhs_ins
.into_iter()
.zip(Value::Functio(rhs).to_string().bytes())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
tape_len: lhs_tl,
},
},
Functio::Able {
params: lhs_params,
body: lhs_body,
} => match rhs.into_functio() {
Functio::Able {
params: rhs_params,
body: rhs_body,
} => Functio::Able {
params: lhs_params
.into_iter()
.zip(rhs_params.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
body: lhs_body
.into_iter()
.zip(rhs_body.into_iter())
.filter_map(|(l, r)| if l != r { Some(l) } else { None })
.collect(),
},
rhs => Value::Int(
Value::Functio(Functio::Able {
params: lhs_params,
body: lhs_body,
})
.into_i32()
- Value::Functio(rhs).into_i32(),
)
.into_functio(),
},
Functio::Chain { functios, .. } => {
let rhs = rhs.into_functio();
let (a, b) = *functios;
match (a == rhs, b == rhs) {
(_, true) => a,
(true, _) => b,
(_, _) => (Value::Functio(a) - Value::Functio(rhs)).into_functio(),
}
}
Functio::Eval(lhs_code) => Functio::Eval(lhs_code.replace(
&match rhs.into_functio() {
Functio::Eval(code) => code,
rhs => Value::Functio(rhs).to_string(),
},
"",
)),
}),
Value::Cart(c) => Value::Cart({
let rhs_cart = rhs.into_cart();
c.into_iter()
.filter(|(k, v)| rhs_cart.get(k) != Some(v))
.collect()
}),
}
}
}
impl ops::Mul for Value {
type Output = Value;
fn mul(self, rhs: Self) -> Self::Output {
match self {
Value::Nul => match rhs {
Value::Nul => Value::Nul,
Value::Str(_) => Value::Str(self.to_string()) * rhs,
Value::Int(_) => Value::Int(self.into_i32()) * rhs,
Value::Bool(_) => Value::Bool(self.into_bool()) * rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) * rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) * rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) * rhs,
},
Value::Str(s) => Value::Str(s.repeat(rhs.into_i32() as usize)),
Value::Int(i) => Value::Int(i.wrapping_mul(rhs.into_i32())),
Value::Bool(b) => Value::Bool(b && rhs.into_bool()),
Value::Abool(_) => {
Value::Abool(Value::Int(self.into_i32().min(rhs.into_i32())).into_abool())
}
Value::Functio(f) => Value::Functio(Functio::Chain {
functios: Box::new((f, rhs.into_functio())),
kind: FunctioChainKind::ByArity,
}),
Value::Cart(c) => {
let rhsc = rhs.into_cart();
Value::Cart(
c.into_iter()
.map(|(k, v)| {
if let Some(k) = rhsc.get(&k) {
(k.borrow().clone(), v)
} else {
(k, v)
}
})
.collect(),
)
}
}
}
}
impl ops::Div for Value {
type Output = Value;
fn div(self, rhs: Self) -> Self::Output {
match self {
Value::Nul => match rhs {
Value::Nul => Value::Nul,
Value::Str(_) => Value::Str(self.to_string()) / rhs,
Value::Int(_) => Value::Int(self.into_i32()) / rhs,
Value::Bool(_) => Value::Bool(self.into_bool()) / rhs,
Value::Abool(_) => Value::Abool(self.into_abool()) / rhs,
Value::Functio(_) => Value::Functio(self.into_functio()) / rhs,
Value::Cart(_) => Value::Cart(self.into_cart()) / rhs,
},
Value::Str(s) => Value::Cart(
s.split(&rhs.to_string())
.enumerate()
.map(|(i, x)| {
(
Value::Int(i as i32 + 1),
Rc::new(RefCell::new(Value::Str(x.to_owned()))),
)
})
.collect(),
),
Value::Int(i) => Value::Int(i.wrapping_div(match rhs.into_i32() {
0 => consts::ANSWER,
x => x,
})),
Value::Bool(b) => Value::Bool(!b || rhs.into_bool()),
Value::Abool(_) => !self + rhs,
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
instructions,
tape_len,
} => {
let fraction = 1.0 / rhs.into_i32() as f64;
let len = instructions.len();
Functio::Bf {
instructions: instructions
.into_iter()
.take((len as f64 * fraction) as usize)
.collect(),
tape_len,
}
}
Functio::Able { params, body } => {
let fraction = 1.0 / rhs.into_i32() as f64;
let len = body.len();
Functio::Able {
params,
body: body
.into_iter()
.take((len as f64 * fraction) as usize)
.collect(),
}
}
Functio::Chain { functios, kind } => {
let functios = *functios;
Functio::Chain {
functios: Box::new((
(Value::Functio(functios.0) / rhs.clone()).into_functio(),
(Value::Functio(functios.1) / rhs).into_functio(),
)),
kind,
}
}
Functio::Eval(s) => {
let fraction = 1.0 / rhs.into_i32() as f64;
let len = s.len();
Functio::Eval(s.chars().take((len as f64 * fraction) as usize).collect())
}
}),
Value::Cart(c) => {
let cart_len = c.len();
let chunk_len = rhs.into_i32() as usize;
Value::Cart(
c.into_iter()
.collect::<Vec<_>>()
.chunks(cart_len / chunk_len + (cart_len % chunk_len != 0) as usize)
.enumerate()
.map(|(k, v)| {
(
Value::Int(k as i32 + 1),
Rc::new(RefCell::new(Value::Cart(v.iter().cloned().collect()))),
)
})
.collect(),
)
}
}
}
}
impl ops::Not for Value {
type Output = Value;
fn not(self) -> Self::Output {
match self {
Value::Nul => Value::Nul,
Value::Str(s) => Value::Str(s.chars().rev().collect()),
Value::Int(i) => Value::Int(i.swap_bytes()),
Value::Bool(b) => Value::Bool(!b),
Value::Abool(a) => Value::Abool(match a {
Abool::Never => Abool::Always,
Abool::Sometimes => Abool::Sometimes,
Abool::Always => Abool::Never,
}),
Value::Functio(f) => Value::Functio(match f {
Functio::Bf {
mut instructions,
tape_len,
} => {
instructions.reverse();
Functio::Bf {
instructions,
tape_len,
}
}
Functio::Able {
mut params,
mut body,
} => {
params.reverse();
body.reverse();
Functio::Able { params, body }
}
Functio::Chain { functios, kind } => {
let (a, b) = *functios;
Functio::Chain {
functios: Box::new((
(!Value::Functio(b)).into_functio(),
(!Value::Functio(a)).into_functio(),
)),
kind,
}
}
Functio::Eval(code) => Functio::Eval(code.chars().rev().collect()),
}),
Value::Cart(c) => Value::Cart(
c.into_iter()
.map(|(k, v)| (v.borrow().clone(), Rc::new(RefCell::new(k))))
.collect(),
),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
let other = other.clone();
match self {
Value::Nul => matches!(other, Value::Nul),
Value::Str(s) => *s == other.to_string(),
Value::Int(i) => *i == other.into_i32(),
Value::Bool(b) => *b == other.into_bool(),
Value::Abool(a) => *a == other.into_abool(),
Value::Functio(f) => *f == other.into_functio(),
Value::Cart(c) => *c == other.into_cart(),
}
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering::*;
let other = other.clone();
match self {
Value::Nul => {
if other == Value::Nul {
Some(Equal)
} else {
None
}
}
Value::Str(s) => Some(s.cmp(&other.to_string())),
Value::Int(i) => Some(i.cmp(&other.into_i32())),
Value::Bool(b) => Some(b.cmp(&other.into_bool())),
Value::Abool(a) => a.partial_cmp(&other.into_abool()),
Value::Functio(_) => self.clone().into_i32().partial_cmp(&other.into_i32()),
Value::Cart(c) => Some(c.len().cmp(&other.into_cart().len())),
}
} }
} }
@ -177,7 +787,7 @@ impl Display for Value {
Value::Bool(v) => write!(f, "{}", v), Value::Bool(v) => write!(f, "{}", v),
Value::Abool(v) => write!(f, "{}", v), Value::Abool(v) => write!(f, "{}", v),
Value::Functio(v) => match v { Value::Functio(v) => match v {
Functio::BfFunctio { Functio::Bf {
instructions, instructions,
tape_len, tape_len,
} => { } => {
@ -189,7 +799,7 @@ impl Display for Value {
.expect("Brainfuck functio source should be UTF-8") .expect("Brainfuck functio source should be UTF-8")
) )
} }
Functio::AbleFunctio { params, body } => { Functio::Able { params, body } => {
write!( write!(
f, f,
"({}) -> {:?}", "({}) -> {:?}",
@ -199,12 +809,34 @@ impl Display for Value {
body, body,
) )
} }
Functio::Chain { functios, kind } => {
let (a, b) = *functios.clone();
write!(
f,
"{} {} {} ",
Value::Functio(a),
match kind {
FunctioChainKind::Equal => '+',
FunctioChainKind::ByArity => '*',
},
Value::Functio(b)
)
}
Functio::Eval(s) => write!(f, "{}", s),
}, },
Value::Cart(c) => { Value::Cart(c) => {
write!(f, "[")?; write!(f, "[")?;
let mut cart_vec = c.iter().collect::<Vec<_>>();
cart_vec.sort_by(|x, y| x.0.partial_cmp(y.0).unwrap_or(std::cmp::Ordering::Less));
for (key, value) in c { for (idx, (key, value)) in cart_vec.into_iter().enumerate() {
write!(f, "{} <= {},", value.borrow(), key)?; write!(
f,
"{}{} <= {}",
if idx != 0 { ", " } else { "" },
value.borrow(),
key
)?;
} }
write!(f, "]") write!(f, "]")