#![feature(let_chains)]
#![feature(if_let_guard)]
#![feature(map_try_insert)]
#![forbid(unsafe_code)]
#![deny(clippy::pedantic)]
#![deny(clippy::nursery)]
#![forbid(clippy::enum_glob_use)]
#![forbid(clippy::unwrap_used)]
use std::{
collections::HashMap,
path::PathBuf,
time::{Duration, Instant},
};
use colored::Colorize;
#[cfg(feature = "silly")]
use geocoding::Reverse;
use itertools::Itertools;
use strum::EnumCount;
use strum_macros::EnumCount as EnumCountMacro;
pub fn report_error(string: &str) -> ! {
panic!("{}: {string}", "ERROR".bold().red());
}
pub fn report_warning(string: &str) {
eprintln!("{}: {string}", "WARNING".bold().yellow());
}
#[derive(Clone, Debug, PartialEq)]
pub enum StackValue {
Integer(i64),
Float(f64),
String(String),
Bool(bool),
}
impl std::fmt::Display for StackValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer(int) => write!(f, "{int}"),
Self::Float(float) => write!(f, "{float:.2}"),
Self::String(string) => write!(f, "{string}"),
Self::Bool(boolean) => write!(f, "{boolean}"),
}
}
}
impl std::ops::Add for StackValue {
type Output = Self;
fn add(self, other: Self) -> Self {
match self {
Self::String(string) => Self::String(string + other.to_string().as_str()),
Self::Integer(int) => match other {
Self::Integer(int2) => Self::Integer(int + int2),
Self::Float(float) => Self::Float(int as f64 + float),
Self::String(string) => Self::String(int.to_string() + string.as_str()),
Self::Bool(boolean) => Self::String(int.to_string() + boolean.to_string().as_str()),
},
Self::Float(float) => match other {
Self::Integer(int) => Self::Float(float + int as f64),
Self::Float(float2) => Self::Float(float + float2),
Self::String(string) => Self::String(self.to_string() + string.as_str()),
Self::Bool(boolean) => Self::String(self.to_string() + boolean.to_string().as_str()),
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => Self::Integer(i64::from(boolean) + i64::from(boolean2)),
Self::String(string) => Self::String(boolean.to_string() + string.as_str()),
Self::Float(_) => Self::String(boolean.to_string() + other.to_string().as_str()),
Self::Integer(int) => Self::String(boolean.to_string() + int.to_string().as_str()),
},
}
}
}
impl std::ops::Sub for StackValue {
type Output = anyhow::Result<Self>;
fn sub(self, other: Self) -> Self::Output {
Ok(match self {
Self::String(ref string) => match other {
Self::Integer(int) => {
if int < 0 {
Self::String(string.to_string() + " ".repeat((-int) as usize).as_str())
} else if int as usize > string.len() {
anyhow::bail!("Tried to subtract int from string where the int is bigger than the strings length");
} else {
Self::String(string[..string.len() - int as usize].to_string())
}
}
Self::Float(float) => {
let int = float.round() as i64;
if int as usize > string.len() {
anyhow::bail!("Tried to subtract float from string where the float is bigger than the strings length");
}
(self - Self::Integer(int))?
}
Self::Bool(bool) => {
let int = i64::from(bool);
if int as usize > string.len() {
anyhow::bail!("Tried to subtract bool from string where the bool is bigger than the strings length");
}
Self::String(string[..string.len() - int as usize].to_string())
}
Self::String(string2) => Self::String(string.replace(string2.as_str(), "")),
},
Self::Integer(int) => match other {
Self::Integer(int2) => Self::Integer(int - int2),
Self::Float(float) => Self::Float(int as f64 - float),
Self::String(string) => Self::String(int.to_string() + " - " + string.as_str()),
Self::Bool(boolean) => Self::Integer(int - i64::from(boolean)),
},
Self::Float(float) => match other {
Self::Integer(int) => Self::Float(float - int as f64),
Self::Float(float2) => Self::Float(float - float2),
Self::String(string) => Self::String(self.to_string() + " - " + string.as_str()),
Self::Bool(boolean) => Self::Float(float - f64::from(boolean)),
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => Self::Integer(i64::from(boolean) - i64::from(boolean2)),
Self::String(string) => Self::String(boolean.to_string() + " - " + string.as_str()),
Self::Float(float) => Self::Float(f64::from(boolean) - float),
Self::Integer(int) => Self::Integer(i64::from(boolean) - int),
},
})
}
}
impl std::ops::Mul for StackValue {
type Output = Self;
fn mul(self, other: Self) -> Self {
match self {
Self::String(string) => match other {
Self::Integer(int) => {
let temp = string.repeat(int.unsigned_abs() as usize);
Self::String(if int < 0 { temp.chars().rev().collect() } else { temp })
}
Self::Float(float) => {
let temp = string.repeat(float.abs().floor() as usize) + &string[0..(string.len() as f64 * float.abs().fract()).round() as usize];
Self::String(if float < 0.0 { temp.chars().rev().collect() } else { temp })
}
Self::Bool(boolean) => Self::String(if boolean { string } else { String::new() }),
Self::String(string2) => Self::String(string2.chars().interleave(string.chars()).collect()),
},
Self::Integer(int) => match other {
Self::Integer(int2) => Self::Integer(int * int2),
Self::Float(float) => Self::Float(int as f64 * float),
Self::String(_) => other * self,
Self::Bool(boolean) => Self::Integer(int * i64::from(boolean)),
},
Self::Float(float) => match other {
Self::Integer(int) => Self::Float(float * int as f64),
Self::Float(float2) => Self::Float(float * float2),
Self::String(_) => other * self,
Self::Bool(boolean) => Self::Float(float * f64::from(boolean)),
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => Self::Integer(i64::from(boolean && boolean2)),
_ => other * self,
},
}
}
}
impl std::ops::Div for StackValue {
type Output = anyhow::Result<either::Either<Self, Vec<Self>>>;
fn div(self, other: Self) -> Self::Output {
Ok(either::Either::Left(match self {
Self::String(string) => match other {
Self::Integer(int) => Self::String(string) * Self::Float(1.0 / int as f64),
Self::Float(float) => Self::String(string) * Self::Float(1.0 / float),
Self::Bool(boolean) => Self::String(if boolean { String::new() } else { string }),
Self::String(string2) => return Ok(either::Either::Right(string.split(string2.as_str()).map(str::to_string).map(Self::String).collect::<Vec<_>>())),
},
Self::Integer(int) => match other {
Self::Integer(int2) => {
if int2 == 0 {
anyhow::bail!("Tried to divide by 0");
}
Self::Float(int as f64 / int2 as f64)
}
Self::Float(float) => {
if float == 0.0 {
anyhow::bail!("Tried to divide by 0")
}
Self::Float(int as f64 / float)
}
Self::String(string) => Self::String(int.to_string() + " / " + string.as_str()),
Self::Bool(boolean) => {
if !boolean {
anyhow::bail!("Tried to divide by false")
}
self
}
},
Self::Float(float) => match other {
Self::Integer(int) => {
if int == 0 {
anyhow::bail!("Tried to divide by 0")
}
Self::Float(float / int as f64)
}
Self::Float(float2) => {
if float2 == 0.0 {
anyhow::bail!("Tried to divide by 0");
}
Self::Float(float / float2)
}
Self::String(string) => Self::String(float.to_string() + " / " + string.as_str()),
Self::Bool(boolean) => {
if !boolean {
anyhow::bail!("Tried to divide by false")
}
self
}
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => {
if !boolean2 {
anyhow::bail!("Tried to divide by false")
}
self
}
Self::String(string) => Self::String(boolean.to_string() + " / " + string.as_str()),
Self::Float(float) => {
if float == 0.0 {
anyhow::bail!("Tried to divide by 0")
}
Self::Float(f64::from(boolean) / float)
}
Self::Integer(int) => {
if int == 0 {
anyhow::bail!("Tried to divide by 0")
}
Self::Float(f64::from(boolean) / int as f64)
}
},
}))
}
}
impl StackValue {
const EPSILON: f64 = 639.4 * std::f64::EPSILON;
pub fn loose_equal(&self, other: &Self) -> bool {
match self {
Self::String(string) => string == &other.to_string(),
Self::Integer(int) => match other {
Self::Integer(int2) => int == int2,
Self::Float(float) => int == &(float.round() as i64),
Self::Bool(boolean) => *int == i64::from(*boolean),
Self::String(_) => other.loose_equal(self),
},
Self::Float(float) => match other {
Self::Float(float2) => float == float2,
Self::Bool(boolean) => float.round() == f64::from(*boolean),
_ => other.loose_equal(self),
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => boolean == boolean2,
_ => other.loose_equal(self),
},
}
}
pub fn strict_equal(&self, other: &Self) -> bool {
match self {
Self::String(string) => matches!(other, Self::String(string2) if string == string2),
Self::Integer(int) => match other {
Self::Integer(int2) => int == int2,
Self::Float(float) => ((*int as f64) - float).abs() < Self::EPSILON,
Self::Bool(boolean) => int == &i64::from(*boolean),
Self::String(_) => other.strict_equal(self),
},
Self::Float(float) => match other {
Self::Float(float2) => (float - float2).abs() < Self::EPSILON,
Self::Bool(_) => false,
_ => other.strict_equal(self),
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => boolean == boolean2,
_ => other.strict_equal(self),
},
}
}
}
impl std::ops::Shr for StackValue {
type Output = anyhow::Result<Self>;
fn shr(self, other: Self) -> Self::Output {
Ok(match self {
Self::String(_) => match other {
Self::String(string2) => match string2.parse() {
Ok(int) => (self >> Self::Integer(int))?,
Err(_) => anyhow::bail!("Couldn't parse string as number"),
},
_ => {
(self - other)? }
},
Self::Integer(int) => match other {
Self::Integer(int2) => Self::Integer(if int2 < 0 { int << (-int2) } else { int >> int2 }),
Self::Float(float) => Self::Float(int as f64 * 0.5_f64.powf(float)),
Self::String(string) => match string.parse::<i64>() {
Ok(int2) => (self >> Self::Integer(int2))?,
Err(_) => anyhow::bail!("Couldn't parse string as number"),
},
Self::Bool(boolean) => Self::Integer(int >> i64::from(boolean)),
},
Self::Float(float) => match other {
Self::Integer(_) => (Self::Integer(i64::from_le_bytes(float.to_bits().to_le_bytes())) >> other)?,
Self::Float(float2) => Self::Float(float * 0.5_f64.powf(float2)),
Self::String(string) => match string.parse::<i64>() {
Ok(int) => (self >> Self::Integer(int))?,
Err(_) => anyhow::bail!("Couldn't parse string as number"),
},
Self::Bool(boolean) => Self::Float(if boolean { float / 2.0 } else { float }),
},
Self::Bool(boolean) => (Self::Integer(i64::from(boolean)) >> other)?,
})
}
}
impl std::ops::Shl for StackValue {
type Output = anyhow::Result<Self>;
fn shl(self, other: Self) -> Self::Output {
Ok(match self {
Self::Integer(int) => match other {
Self::Integer(int2) => (self >> Self::Integer(-int2))?,
Self::Float(float) => Self::Float(int as f64 * 2_f64.powf(float)),
Self::String(string) => match string.parse::<i64>() {
Ok(int2) => (self << Self::Integer(int2))?,
Err(_) => anyhow::bail!("Couldn't parse string as number"),
},
Self::Bool(boolean) => Self::Integer(int << i64::from(boolean)),
},
Self::Float(float) => match other {
Self::Integer(_) => (Self::Integer(i64::from_le_bytes(float.to_bits().to_le_bytes())) << other)?,
Self::Float(float2) => Self::Float(float * 2.0_f64.powf(float2)),
Self::String(string) => match string.parse::<i64>() {
Ok(int) => (self << Self::Integer(int))?,
Err(_) => anyhow::bail!("Couldn't parse string as number"),
},
Self::Bool(boolean) => Self::Float(if boolean { float * 2.0 } else { float }),
},
Self::String(_) => self + other,
Self::Bool(boolean) => (Self::Integer(i64::from(boolean)) << other)?,
})
}
}
impl std::ops::BitOr for StackValue {
type Output = Self;
fn bitor(self, other: Self) -> Self::Output {
use itertools::EitherOrBoth as E;
match self {
Self::Integer(int) => match other {
Self::Integer(int2) => Self::Integer(int | int2),
Self::Float(float) => Self::Integer(int | i64::from_le_bytes(float.to_le_bytes())),
Self::String(_) => Self::String(format!("{int:064b}")) | other,
Self::Bool(boolean) => Self::Bool((int.abs() > 0) || boolean),
},
Self::Float(float) => match other {
Self::Integer(_) => other | self,
Self::Float(float2) => Self::Integer(i64::from_be_bytes(float.to_be_bytes()) | i64::from_ne_bytes(float2.to_ne_bytes())),
Self::String(_) => Self::String(format!("{:064b}", i64::from_le_bytes(float.to_le_bytes()))) | other,
Self::Bool(boolean) => Self::Bool((float.abs() > 0.0) || boolean),
},
Self::String(ref string) => match other {
Self::String(string2) => Self::String(
string
.chars()
.zip_longest(string2.chars())
.map(|v| match v {
E::Both(a, b) => a.max(b),
E::Left(a) => a,
E::Right(b) => b,
})
.collect(),
),
Self::Bool(boolean) => Self::Bool(!string.is_empty() || boolean),
_ => other | self,
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => Self::Bool(boolean || boolean2),
_ => other | self,
},
}
}
}
impl std::ops::BitAnd for StackValue {
type Output = Self;
fn bitand(self, other: Self) -> Self::Output {
use itertools::EitherOrBoth as E;
match self {
Self::Integer(int) => match other {
Self::Integer(int2) => Self::Integer(int & int2),
Self::Float(float) => Self::Integer(int & i64::from_le_bytes(float.to_le_bytes())),
Self::String(_) => Self::String(format!("{int:064b}")) & other,
Self::Bool(boolean) => Self::Bool((int.abs() > 0) && boolean),
},
Self::Float(float) => match other {
Self::Integer(_) => other & self,
Self::Float(float2) => Self::Integer(i64::from_be_bytes(float.to_be_bytes()) & i64::from_ne_bytes(float2.to_ne_bytes())),
Self::String(_) => Self::String(format!("{:064b}", i64::from_le_bytes(float.to_le_bytes()))) & other,
Self::Bool(boolean) => Self::Bool((float.abs() > 0.0) && boolean),
},
Self::String(ref string) => match other {
Self::String(string2) => Self::String(
string
.chars()
.zip_longest(string2.chars())
.map(|v| match v {
E::Both(a, b) => {
if a == b {
a
} else {
' '
}
}
_ => ' ',
})
.collect(),
),
Self::Bool(boolean) => Self::Bool(!string.is_empty() && boolean),
_ => other & self,
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => Self::Bool(boolean && boolean2),
_ => other & self,
},
}
}
}
impl std::ops::BitXor for StackValue {
type Output = Self;
fn bitxor(self, other: Self) -> Self::Output {
use itertools::EitherOrBoth as E;
match self {
Self::Integer(int) => match other {
Self::Integer(int2) => Self::Integer(int ^ int2),
Self::Float(float) => Self::Integer(int ^ i64::from_le_bytes(float.to_le_bytes())),
Self::String(_) => Self::String(format!("{int:064b}")) ^ other,
Self::Bool(boolean) => Self::Bool((int.abs() > 0) != boolean),
},
Self::Float(float) => match other {
Self::Integer(_) => other ^ self,
Self::Float(float2) => Self::Integer(i64::from_be_bytes(float.to_be_bytes()) ^ i64::from_ne_bytes(float2.to_ne_bytes())),
Self::String(_) => Self::String(format!("{:064b}", i64::from_le_bytes(float.to_le_bytes()))) ^ other,
Self::Bool(boolean) => Self::Bool((float.abs() > 0.0) != boolean),
},
Self::String(ref string) => match other {
Self::String(string2) => Self::String(
string
.chars()
.zip_longest(string2.chars())
.map(|v| match v {
E::Both(a, b) => {
if a == ' ' {
b
} else if b == ' ' {
a
} else {
' '
}
}
E::Left(c) | E::Right(c) => c,
})
.collect(),
),
Self::Bool(boolean) => Self::Bool(!string.is_empty() != boolean),
_ => other ^ self,
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => Self::Bool(boolean != boolean2),
_ => other ^ self,
},
}
}
}
impl std::ops::Not for StackValue {
type Output = Self;
fn not(self) -> Self::Output {
match self {
Self::Integer(int) => Self::Integer(!int),
Self::Float(float) => Self::Integer(!i64::from_be_bytes(float.to_be_bytes())),
Self::String(string) => Self::String(
string
.chars()
.map(|c| (c.to_ascii_lowercase(), c.is_uppercase()))
.map(|(c, b)| {
(
match c {
'a'..='z' => (((c as u8 - b'a' + 13) % 26) + b'a') as char,
_ => c,
},
b,
)
})
.map(|(c, b)| if b { c.to_ascii_uppercase() } else { c })
.collect(),
),
Self::Bool(boolean) => Self::Bool(!boolean),
}
}
}
impl std::cmp::PartialOrd for StackValue {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match self {
Self::String(string) => string.partial_cmp(&other.to_string()),
Self::Integer(int) => match other {
Self::Integer(int2) => int.partial_cmp(int2),
Self::Float(float) => ((*int) as f64).partial_cmp(float),
Self::Bool(boolean) => int.partial_cmp(&i64::from(*boolean)),
Self::String(string) => int.to_string().partial_cmp(string),
},
Self::Float(float) => match other {
Self::Float(float2) => float.partial_cmp(float2),
Self::Integer(int) => float.partial_cmp(&(*int as f64)),
Self::Bool(boolean) => float.partial_cmp(&(f64::from(*boolean))),
Self::String(string) => float.to_string().partial_cmp(string),
},
Self::Bool(boolean) => match other {
Self::Bool(boolean2) => boolean.partial_cmp(boolean2),
Self::Integer(int) => i64::from(*boolean).partial_cmp(int),
Self::Float(float) => f64::from(*boolean).partial_cmp(float),
Self::String(string) => boolean.to_string().partial_cmp(string),
},
}
}
}
#[derive(EnumCountMacro, Clone, PartialEq, Debug)]
pub enum Token {
StackValue(StackValue),
Add,
Subtract,
Multiply,
Divide,
Dup,
Drop,
Swap,
Over,
Rot,
Print,
Println,
If(usize),
Elif(usize),
Else(usize),
MrBeast(usize),
Eq,
Seq,
Sseq,
Ineq,
Sineq,
Ssineq,
Gt,
Lt,
Ge,
Gse,
Gsse,
Le,
Lse,
Lsse,
Shr,
Shl,
Or,
And,
Not,
Xor,
Dummy,
}
pub fn parse_file(path: &PathBuf) -> anyhow::Result<Vec<Token>> {
let contents = match std::fs::read_to_string(path) {
Ok(string) => string,
Err(err) => {
report_error(format!("File could not be read because {err}").as_str());
}
};
parse_string(contents)
}
pub fn parse_string(mut contents: String) -> anyhow::Result<Vec<Token>> {
contents += " "; let mut tokens = Vec::new();
let mut if_statements: Vec<usize> = Vec::with_capacity(4); let mut elif_statements: HashMap<usize, usize> = HashMap::new();
let mut mr_beast_statements: HashMap<usize, Vec<usize>> = HashMap::new();
let mut else_statements: Vec<usize> = Vec::with_capacity(3);
let mut is_commenting = false;
let mut is_stringing = false;
let mut word = String::new();
let mut index = 0;
let mut chars = contents.chars();
while let Some(chr) = chars.next() {
if chr == '\'' && !is_commenting {
if is_stringing {
tokens.push(Token::StackValue(StackValue::String(word)));
word = String::new();
index += 1;
}
is_stringing = !is_stringing;
continue;
}
if is_stringing {
if chr == '\\' {
word.push(match chars.next() {
Some('n') => '\n',
Some('r') => '\r',
Some('t') => '\t',
Some('\\') => '\\',
Some('0') => '\0',
Some('\'') => '\'',
Some(x) => anyhow::bail!(format!("Unexpected escape character '{x}'")),
None => anyhow::bail!("Expected escape character, found end of file"),
});
} else {
word.push(chr);
}
continue;
}
if !chr.is_whitespace() {
word.push(chr);
continue;
}
if chr.is_whitespace() && word.is_empty() {
continue;
}
if !is_commenting {
static_assertions::const_assert_eq!(Token::COUNT, 37);
let token = match word.as_str() {
"+" => Token::Add,
"-" => Token::Subtract,
"*" => Token::Multiply,
"/" => Token::Divide,
"=" => Token::Eq,
"==" => Token::Seq,
"===" => Token::Sseq,
"!=" => Token::Ineq,
"!==" => Token::Sineq,
"!===" => Token::Ssineq,
">" => Token::Gt,
"<" => Token::Lt,
"=>" => Token::Ge,
"==>" => Token::Gse,
"===>" => Token::Gsse,
"<=" => Token::Le,
"<==" => Token::Lse,
"<===" => Token::Lsse,
">>" => Token::Shr,
"<<" => Token::Shl,
"or" => Token::Or,
"and" => Token::And,
"not" => Token::Not,
"xor" => Token::Xor,
"if" => {
if_statements.push(index);
Token::If(usize::MAX)
}
"elif" => {
let Some(if_index) = if_statements.last() else {
anyhow::bail!("Found 'elif' without 'if'");
};
if elif_statements.try_insert(*if_index, index).is_err() {
anyhow::bail!("Found two 'elif's next to eachother without 'MrBeast' between them")
}
Token::Elif(usize::MAX)
}
"MrBeast!" => {
let Some(if_index) = if_statements.last() else {
anyhow::bail!("Found 'MrBeast' closing an if-statement that doesn't exist");
};
if let Some(mr_beasts) = mr_beast_statements.get_mut(if_index) {
mr_beasts.push(index);
if let Some(elif_index) = elif_statements.remove(if_index)
&& let Some(Token::Elif(jump_addr)) = tokens.get_mut(elif_index)
{
*jump_addr = index + 1;
} else {
anyhow::bail!("This is embarrassing");
}
} else {
mr_beast_statements.insert(*if_index, vec![index]);
if let Some(Token::If(jump_addr)) = tokens.get_mut(*if_index) {
*jump_addr = index + 1;
} else {
anyhow::bail!("This is embarrassing");
}
}
Token::MrBeast(usize::MAX)
}
"else" => {
let Some(if_index) = if_statements.last() else {
anyhow::bail!("Found 'else' without match 'if'");
};
if let Some(elif_index) = elif_statements.remove(if_index) {
if let Some(Token::Elif(jump_addr)) = tokens.get_mut(elif_index) {
*jump_addr = index + 1;
} else {
anyhow::bail!("This is embarrassing");
}
} else if let Some(Token::If(jump_addr)) = tokens.get_mut(*if_index) {
*jump_addr = index + 1;
} else {
anyhow::bail!("This is embarrassing");
}
else_statements.push(index);
Token::Else(usize::MAX)
}
"fi" => {
let Some(if_index) = if_statements.pop() else {
anyhow::bail!("Found 'fi' closing an if-statement that doesn't exist");
};
if let Some(mr_beasts) = mr_beast_statements.remove(&if_index) {
for mr_beast_index in mr_beasts {
if let Some(Token::MrBeast(jump_addr)) = tokens.get_mut(mr_beast_index) {
*jump_addr = index;
}
}
} else if let Some(Token::If(jump_addr)) = tokens.get_mut(if_index) {
if *jump_addr == usize::MAX {
*jump_addr = index;
}
} else {
anyhow::bail!("This is embarrassing");
}
if let Some(else_index) = else_statements.pop() {
let Some(Token::Else(else_statement)) = tokens.get_mut(else_index) else {
anyhow::bail!("This is embarrassing");
};
*else_statement = index;
}
Token::Dummy
}
"dup" => Token::Dup,
"drop" => Token::Drop,
"swap" => Token::Swap,
"over" => Token::Over,
"rot" => Token::Rot,
"print" => Token::Print,
"println" => Token::Println,
"comment" => {
is_commenting = true;
Token::Dummy }
x if let Ok(int) = x.parse::<i64>() => Token::StackValue(StackValue::Integer(int)),
x if let Ok(float) = x.parse::<f64>() => Token::StackValue(StackValue::Float(float)),
x if let Ok(boolean) = x.parse::<bool>() => Token::StackValue(StackValue::Bool(boolean)),
x if x.len() == 3 && x.chars().next().is_some_and(|c| c == '"') && x.chars().last().is_some_and(|c| c == '"') => {
Token::StackValue(StackValue::Integer(x[1..x.len() - 1].chars().next().expect("This should work") as i64))
}
unrecognized => {
anyhow::bail!(format!("Unrecognized token {unrecognized}",));
}
};
tokens.push(token);
}
if word == "no_comment" {
is_commenting = false;
}
word.clear();
index += 1;
}
if !if_statements.is_empty() {
anyhow::bail!("Unclosed if-statement");
}
if !else_statements.is_empty() {
anyhow::bail!("This shouldn't happen: Dangling else-statement");
}
if !mr_beast_statements.is_empty() {
anyhow::bail!("This shouldn't happen: Dangling MrBeast-statements");
}
if is_commenting {
anyhow::bail!("Unclosed comment");
}
Ok(tokens)
}
pub fn execute_tokens<T: std::io::Write>(tokens: &[Token], #[cfg(feature = "silly")] out_of_free_runs: bool, writable: &mut T, time_limit: Option<Duration>) -> anyhow::Result<Vec<StackValue>> {
#[cfg(feature = "silly")]
if out_of_free_runs {
let local_ip = local_ip_address::local_ip().expect("I'm so done").to_string();
let info = geolocation::find(local_ip.as_str()).expect("What");
let (longitude, latitude) = (info.longitude.parse::<f64>().unwrap_or(180.0), info.latitude.parse::<f64>().unwrap_or(0.0));
let openstreetmap = geocoding::Openstreetmap::new();
let location = openstreetmap.reverse(&geocoding::Point::new(-longitude, 180.0 - latitude));
println!(
"Connecting to our servers in {}, our datacenter that is nearest to you!",
location.unwrap_or_else(|_| Some("Antarctica".to_string())).unwrap_or_else(|| "Antarctica".to_string())
);
std::thread::sleep(std::time::Duration::from_secs(1));
}
let mut stack: Vec<StackValue> = Vec::new();
let mut i: usize = 0;
let start = Instant::now();
while let Some(token) = tokens.get(i) {
#[cfg(feature = "silly")]
if out_of_free_runs {
std::thread::sleep(std::time::Duration::from_millis(300));
}
if let Some(limit) = time_limit
&& start - Instant::now() > limit
{
anyhow::bail!("Exceeded time limit!");
}
static_assertions::const_assert_eq!(Token::COUNT, 37);
match token {
Token::Dummy => {}
Token::StackValue(x) => stack.push(x.clone()),
Token::Add => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(b + a);
} else {
anyhow::bail!("The stack must contain at least two elements for an addition to be made");
}
}
Token::Subtract => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push((b - a)?);
} else {
anyhow::bail!("The stack must contain at least two elements for a subtraction to be made");
}
}
Token::Multiply => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(a * b);
} else {
anyhow::bail!("The stack must contain at least two elements for a multiplication to be made");
}
}
Token::Divide => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
match (b / a)? {
either::Either::Left(sv) => stack.push(sv),
either::Either::Right(svs) => stack.extend(svs),
}
} else {
anyhow::bail!("The stack must contain at least two elements for a division to be made");
}
}
Token::Eq => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(a.loose_equal(&b)));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Ineq => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(!a.loose_equal(&b)));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Seq => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(a.strict_equal(&b)));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Sineq => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(!a.loose_equal(&b)));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Sseq => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(a == b))
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Ssineq => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(a != b))
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Gt => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(b > a));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Lt => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(b < a));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Ge => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(b > a || b.loose_equal(&a)));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Le => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(b < a || b.loose_equal(&a)));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Gse => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(b > a || b.strict_equal(&a)));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Lse => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(b < a || b.strict_equal(&a)));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Gsse => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(b > a || b == a));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Lsse => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(StackValue::Bool(b < a || b == a));
} else {
anyhow::bail!("The stack must contain at least two elements for them to be compared");
}
}
Token::Shr => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push((b >> a)?);
} else {
anyhow::bail!("The stack must contain at least two elements for them to be manipulated");
}
}
Token::Shl => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push((b << a)?);
} else {
anyhow::bail!("The stack must contain at least two elements for them to be manipulated");
}
}
Token::Or => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(b | a);
} else {
anyhow::bail!("The stack must contain at least two elements for them to be manipulated");
}
}
Token::And => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(b & a);
} else {
anyhow::bail!("The stack must contain at least two elements for them to be manipulated");
}
}
Token::Not => {
if let Some(a) = stack.pop() {
stack.push(!a);
} else {
anyhow::bail!("The stack must contain at least one element for it to be negated");
}
}
Token::Xor => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(b ^ a);
} else {
anyhow::bail!("The stack must contain at least two elements for them to be manipulated");
}
}
Token::Dup => {
if let Some(a) = stack.last() {
stack.push(a.clone());
} else {
anyhow::bail!("The stack must contain at least one element for it to be duplicated");
}
}
Token::Drop => {
if stack.is_empty() {
anyhow::bail!("The stack must contain at least one element for it to be dropped");
}
let _ = stack.pop();
}
Token::Swap => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(a);
stack.push(b);
} else {
anyhow::bail!("The stack must contain at least two elements for them to be swapped");
}
}
Token::Over => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
{
stack.push(b.clone());
stack.push(a);
stack.push(b);
} else {
anyhow::bail!("The stack must contain at least two elements for them to be overed");
}
}
Token::Rot => {
if let Some(a) = stack.pop()
&& let Some(b) = stack.pop()
&& let Some(c) = stack.pop()
{
stack.push(b);
stack.push(a);
stack.push(c);
} else {
anyhow::bail!("The stack must contain at least three elements for them to be roted");
}
}
Token::Print => {
if let Some(a) = stack.pop() {
if let Err(err) = write!(writable, "{a}") {
anyhow::bail!("Couldn't write to writable because {err}");
};
} else {
anyhow::bail!("The stack must contain at least one element for it to be printed");
}
}
Token::Println => {
if let Some(a) = stack.pop() {
if let Err(err) = writeln!(writable, "{a}") {
anyhow::bail!("Couldn't write to writable because {err}");
}
} else {
anyhow::bail!("The stack must contain at least one element for it to be printed");
}
}
Token::If(jump_addr) | Token::Elif(jump_addr) => {
if let Some(StackValue::Bool(boolean)) = stack.pop() {
if !boolean {
i = *jump_addr;
continue;
}
} else {
anyhow::bail!("If needs one boolean to be on the stack");
}
}
Token::MrBeast(jump_addr) | Token::Else(jump_addr) => {
i = *jump_addr;
continue;
} }
i += 1;
}
eprintln!();
Ok(stack)
}