// A code block is any section of code. It contains its own local variables and functions, as well as a list of statements. // Types starting with S are directly parsed from Strings and unchecked. Types starting with T are type-checked templates for R-types. Types starting with R are runnable. S are converted to T after parsing is done, and T are converted to R whenever they need to run. use std::{ fmt::Display, sync::{Arc, Mutex}, }; use self::to_runnable::ToRunnableError; use super::{ builtins::BuiltinFunction, val_data::{VData, VDataEnum}, val_type::{VSingleType, VType}, }; // Represents a block of code #[derive(Debug)] pub struct SBlock { pub statements: Vec, } impl SBlock { pub fn new(statements: Vec) -> Self { Self { statements } } } // A function is a block of code that starts with some local variables as inputs and returns some value as its output. The last statement in the block will be the output. #[derive(Debug)] pub struct SFunction { inputs: Vec<(String, VType)>, block: SBlock, } impl SFunction { pub fn new(inputs: Vec<(String, VType)>, block: SBlock) -> Self { Self { inputs, block } } } #[derive(Debug)] pub struct SStatement { pub output_to: Option, pub statement: Box, } impl SStatement { pub fn new(statement: SStatementEnum) -> Self { Self { output_to: None, statement: Box::new(statement), } } pub fn output_to(mut self, var: String) -> Self { self.output_to = Some(var); self } } #[derive(Debug)] pub enum SStatementEnum { Value(VData), Tuple(Vec), List(Vec), Variable(String, bool), FunctionCall(String, Vec), FunctionDefinition(Option, SFunction), Block(SBlock), If(SStatement, SStatement, Option), While(SStatement), For(String, SStatement, SStatement), Switch(String, Vec<(VType, SStatement)>, bool), Match(String, Vec<(SStatement, SStatement)>), IndexFixed(SStatement, usize), } impl Into for SStatementEnum { fn into(self) -> SStatement { SStatement::new(self) } } // Conversion type Am = Arc>; fn am(i: T) -> Am { Arc::new(Mutex::new(i)) } pub fn to_runnable(f: SFunction) -> Result { to_runnable::to_runnable(f) } pub mod to_runnable { use std::{ collections::HashMap, fmt::{Debug, Display}, sync::Arc, }; use crate::script::{ val_data::VDataEnum, val_type::{VSingleType, VType}, }; use super::{ BuiltinFunction, RBlock, RFunction, RScript, RStatement, RStatementEnum, SBlock, SFunction, SStatement, SStatementEnum, }; pub enum ToRunnableError { MainWrongInput, UseOfUndefinedVariable(String), UseOfUndefinedFunction(String), FunctionWrongArgCount(String, usize, usize), InvalidType { expected: VType, found: VType, problematic: Vec, }, InvalidTypeForWhileLoop(VType), CaseForceButTypeNotCovered(VType), MatchConditionInvalidReturn(VType), NotIndexableFixed(VType, usize), } impl Debug for ToRunnableError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{self}") } } impl Display for ToRunnableError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::MainWrongInput => write!( f, "Main function had the wrong input. This is a bug and should never happen." ), Self::UseOfUndefinedVariable(v) => write!(f, "Cannot use variable \"{v}\" as it isn't defined (yet?)."), Self::UseOfUndefinedFunction(v) => write!(f, "Cannot use function \"{v}\" as it isn't defined (yet?)."), Self::FunctionWrongArgCount(v, a, b) => write!(f, "Tried to call function \"{v}\", which takes {a} arguments, with {b} arguments instead."), Self::InvalidType { expected, found, problematic, } => { write!(f, "Invalid type: Expected {expected:?} but found {found:?}, which includes {problematic:?}, which is not covered.") } Self::InvalidTypeForWhileLoop(v) => write!(f, "Invalid type: Expected bool or Tuples of length 0 or 1 as return types for the while loop, but found {v:?} instead."), Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"), Self::MatchConditionInvalidReturn(v) => write!(f, "match statement condition returned {v}, which is not necessarily a tuple of size 0 to 1."), Self::NotIndexableFixed(t, i) => write!(f, "Cannot use fixed-index {i} on type {t}."), } } } // Global, shared between all struct GInfo { vars: usize, } // Local, used to keep local variables separated #[derive(Clone)] struct LInfo { vars: HashMap, fns: HashMap>, } pub fn to_runnable(s: SFunction) -> Result { if s.inputs.len() != 1 || s.inputs[0].0 != "args" { return Err(ToRunnableError::MainWrongInput); } if s.inputs[0].1 != (VType { types: vec![VSingleType::List(VType { types: vec![VSingleType::String], })], }) {} let mut ginfo = GInfo { vars: 0 }; let func = function( &s, &mut ginfo, LInfo { vars: HashMap::new(), fns: HashMap::new(), }, )?; Ok(RScript::new(func, ginfo.vars)?) } // go over every possible known-type input for the given function, returning all possible RFunctions. fn get_all_functions( s: &SFunction, ginfo: &mut GInfo, linfo: &mut LInfo, input_vars: &Vec, inputs: &mut Vec, out: &mut Vec<(Vec, VType)>, ) -> Result<(), ToRunnableError> { if s.inputs.len() > inputs.len() { let input_here = &s.inputs[inputs.len()].1; for t in &input_here.types { inputs.push(t.clone().into()); get_all_functions(s, ginfo, linfo, input_vars, inputs, out)?; inputs.pop(); } Ok(()) } else { // set the types for (varid, vartype) in s.inputs.iter().zip(inputs.iter()) { linfo.vars.get_mut(&varid.0).unwrap().1 = vartype.clone().into(); } out.push((inputs.clone(), block(&s.block, ginfo, linfo.clone())?.out())); Ok(()) } } fn function( s: &SFunction, ginfo: &mut GInfo, mut linfo: LInfo, ) -> Result { let mut input_vars = vec![]; let mut input_types = vec![]; for (iname, itype) in &s.inputs { linfo .vars .insert(iname.clone(), (ginfo.vars, itype.clone())); input_vars.push(ginfo.vars); input_types.push(itype.clone()); ginfo.vars += 1; } let mut all_outs = vec![]; get_all_functions( s, ginfo, &mut linfo, &input_vars, &mut Vec::with_capacity(s.inputs.len()), &mut all_outs, )?; // set the types to all possible types (get_all_functions sets the types to one single type to get the return type of the block for that case) for (varid, vartype) in s.inputs.iter().zip(input_types.iter()) { linfo.vars.get_mut(&varid.0).unwrap().1 = vartype.clone(); } Ok(RFunction { inputs: input_vars, input_types, input_output_map: all_outs, block: block(&s.block, ginfo, linfo)?, }) } fn block(s: &SBlock, ginfo: &mut GInfo, mut linfo: LInfo) -> Result { let mut statements = Vec::new(); for st in &s.statements { statements.push(statement(st, ginfo, &mut linfo)?); } Ok(RBlock { statements }) } fn statement( s: &SStatement, ginfo: &mut GInfo, linfo: &mut LInfo, ) -> Result { let mut statement = match &*s.statement { SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()), SStatementEnum::Tuple(v) | SStatementEnum::List(v) => { let mut w = Vec::with_capacity(v.len()); for v in v { w.push(statement(v, ginfo, linfo)?); } if let SStatementEnum::List(_) = &*s.statement { RStatementEnum::List(w) } else { RStatementEnum::Tuple(w) } } SStatementEnum::Variable(v, is_ref) => { if let Some(var) = linfo.vars.get(v) { RStatementEnum::Variable(var.0, var.1.clone(), *is_ref) } else { return Err(ToRunnableError::UseOfUndefinedVariable(v.clone())); } } SStatementEnum::FunctionCall(v, args) => { let mut rargs = Vec::with_capacity(args.len()); for arg in args.iter() { rargs.push(statement(arg, ginfo, linfo)?); } if let Some(func) = linfo.fns.get(v) { if rargs.len() != func.inputs.len() { return Err(ToRunnableError::FunctionWrongArgCount( v.clone(), func.inputs.len(), rargs.len(), )); } for (i, rarg) in rargs.iter().enumerate() { let rarg = rarg.out(); let out = rarg.fits_in(&func.input_types[i]); if !out.is_empty() { return Err(ToRunnableError::InvalidType { expected: func.input_types[i].clone(), found: rarg, problematic: out, }); } } RStatementEnum::FunctionCall(func.clone(), rargs) } else { // TODO: type-checking for builtins if let Some(builtin) = BuiltinFunction::get(v) { if builtin.can_take(&rargs.iter().map(|v| v.out()).collect()) { RStatementEnum::BuiltinFunction(builtin, rargs) } else { todo!("ERR: Builtin function \"{v}\" with wrong args - this isn't a proper error yet, sorry."); } } else { return Err(ToRunnableError::UseOfUndefinedFunction(v.clone())); } } } SStatementEnum::FunctionDefinition(name, f) => { if let Some(name) = name { // named function => add to global functions linfo .fns .insert(name.clone(), Arc::new(function(f, ginfo, linfo.clone())?)); RStatementEnum::Value(VDataEnum::Tuple(vec![]).to()) } else { // anonymous function => return as value RStatementEnum::Value( VDataEnum::Function(function(f, ginfo, linfo.clone())?).to(), ) } } SStatementEnum::Block(b) => RStatementEnum::Block(block(&b, ginfo, linfo.clone())?), SStatementEnum::If(c, t, e) => RStatementEnum::If( { let condition = statement(&c, ginfo, linfo)?; let out = condition.out().fits_in(&VType { types: vec![VSingleType::Bool], }); if out.is_empty() { condition } else { return Err(ToRunnableError::InvalidType { expected: VSingleType::Bool.into(), found: condition.out(), problematic: out, }); } }, statement(&t, ginfo, linfo)?, match e { Some(v) => Some(statement(&v, ginfo, linfo)?), None => None, }, ), SStatementEnum::While(c) => RStatementEnum::While({ let condition = statement(&c, ginfo, linfo)?; let out = condition.out(); let out1 = out.fits_in(&VSingleType::Bool.into()); if out1.is_empty() { condition } else { if out.types.is_empty() { return Err(ToRunnableError::InvalidTypeForWhileLoop(out)); } for t in out.types.iter() { if let VSingleType::Tuple(v) = t { if v.len() > 1 { return Err(ToRunnableError::InvalidTypeForWhileLoop(out)); } } else { return Err(ToRunnableError::InvalidTypeForWhileLoop(out)); } } condition } }), SStatementEnum::For(v, c, b) => { let mut linfo = linfo.clone(); let container = statement(&c, ginfo, &mut linfo)?; linfo .vars .insert(v.clone(), (ginfo.vars, container.out().inner_types())); let for_loop_var = ginfo.vars; ginfo.vars += 1; let block = statement(&b, ginfo, &mut linfo)?; let o = RStatementEnum::For(for_loop_var, container, block); o } SStatementEnum::Switch(switch_on, cases, force) => { if let Some(switch_on_v) = linfo.vars.get(switch_on).cloned() { let mut ncases = Vec::with_capacity(cases.len()); let og_type = switch_on_v.1.clone(); // linfo.vars.get(switch_on).unwrap().1.clone(); for case in cases { linfo.vars.get_mut(switch_on).unwrap().1 = case.0.clone(); ncases.push((case.0.clone(), statement(&case.1, ginfo, linfo)?)); } linfo.vars.get_mut(switch_on).unwrap().1 = og_type; let switch_on_out = switch_on_v.1; if *force { let mut types_not_covered_req_error = false; let mut types_not_covered = VType { types: vec![] }; for val_type in switch_on_out.types.iter() { let val_type: VType = val_type.clone().into(); let mut linf2 = linfo.clone(); linf2.vars.get_mut(switch_on).unwrap().1 = val_type.clone(); 'force: { for (case_type, _) in cases { if val_type.fits_in(&case_type).is_empty() { break 'force; } } types_not_covered_req_error = true; types_not_covered = types_not_covered | val_type; } } if types_not_covered_req_error { return Err(ToRunnableError::CaseForceButTypeNotCovered(types_not_covered)); } } RStatementEnum::Switch( RStatementEnum::Variable(switch_on_v.0, switch_on_out, false).to(), ncases, ) } else { return Err(ToRunnableError::UseOfUndefinedVariable(switch_on.clone())); } } SStatementEnum::Match(match_on, cases) => { if let Some(switch_on_v) = linfo.vars.get(match_on).cloned() { let mut ncases = Vec::with_capacity(cases.len()); let og_type = switch_on_v.1.clone(); // linfo.vars.get(match_on).unwrap().1.clone(); for case in cases { let case_condition = statement(&case.0, ginfo, linfo)?; let case_condition_out = case_condition.out(); let mut refutable = false; let mut success_output = VType { types: vec![] }; for case_type in case_condition_out.types.iter() { match case_type { VSingleType::Tuple(tuple) => match tuple.len() { 0 => refutable = true, 1 => success_output = success_output | &tuple[0], _ => return Err(ToRunnableError::MatchConditionInvalidReturn(case_condition_out)), }, VSingleType::Bool => { refutable = true; success_output = success_output | VSingleType::Bool.to() } _ => success_output = success_output | case_type.clone().to(), } } if refutable == false { eprintln!("WARN: Irrefutable match condition with return type {}", case_condition_out); } if !success_output.types.is_empty() { let var = linfo.vars.get_mut(match_on).unwrap(); let og = var.1.clone(); var.1 = success_output; let case_action = statement(&case.1, ginfo, linfo)?; linfo.vars.get_mut(match_on).unwrap().1 = og; ncases.push((case_condition, case_action)); } else { eprintln!("WARN: Match condition with return type {} never returns a match and will be ignored entirely. Note: this also skips type-checking for the action part of this match arm because the success type is not known.", case_condition_out); } } linfo.vars.get_mut(match_on).unwrap().1 = og_type; RStatementEnum::Match( switch_on_v.0, ncases, ) } else { return Err(ToRunnableError::UseOfUndefinedVariable(match_on.clone())); } } SStatementEnum::IndexFixed(st, i) => { let st = statement(st, ginfo, linfo)?; let ok = 'ok: { let mut one = false; for t in st.out().types { one = true; // only if all types are indexable by i match t { VSingleType::Tuple(v) => { if v.len() <= *i { break 'ok false; } } _ => break 'ok false, } } one }; if ok { RStatementEnum::IndexFixed(st, *i) } else { return Err(ToRunnableError::NotIndexableFixed(st.out(), *i)); } } } .to(); if let Some(opt) = &s.output_to { if let Some(var) = linfo.vars.get(opt) { let out = statement.out(); let var_id = var.0; let var_out = &var.1; let inv_types = out.fits_in(&var_out); if !inv_types.is_empty() { eprintln!("Warn: shadowing variable {opt} because statement's output type {out} does not fit in the original variable's {var_out}. This might become an error in the future, or it might stop shadowing the variiable entirely - for stable scripts, avoid this by giving the variable a different name."); linfo.vars.insert(opt.clone(), (ginfo.vars, out)); statement.output_to = Some(ginfo.vars); ginfo.vars += 1; } else { statement.output_to = Some(var_id); } } else { linfo .vars .insert(opt.clone(), (ginfo.vars, statement.out())); statement.output_to = Some(ginfo.vars); ginfo.vars += 1; } } Ok(statement) } } // Runnable #[derive(Clone, Debug)] pub struct RBlock { statements: Vec, } impl RBlock { pub fn run(&self, vars: &Vec>) -> VData { let mut last = None; for statement in &self.statements { last = Some(statement.run(vars)); } if let Some(v) = last { v } else { VDataEnum::Tuple(vec![]).to() } } pub fn out(&self) -> VType { if let Some(last) = self.statements.last() { last.out() } else { VType { types: vec![VSingleType::Tuple(vec![])], } } } } #[derive(Clone, Debug)] pub struct RFunction { pub inputs: Vec, pub input_types: Vec, pub input_output_map: Vec<(Vec, VType)>, pub block: RBlock, } impl RFunction { pub fn run(&self, vars: &Vec>) -> VData { self.block.run(vars) } pub fn out(&self, input_types: &Vec) -> VType { self.input_output_map .iter() .find_map(|v| { if v.0 == *input_types { Some(v.1.clone()) } else { None } }) .expect("invalid args for function! possible issue with type-checker if this can be reached! feel free to report a bug.") } pub fn out_vt(&self, input_types: &Vec) -> VType { let mut out = VType { types: vec![] }; for (itype, otype) in self.input_output_map.iter() { if itype .iter() .zip(input_types.iter()) .all(|(expected, got)| got.contains(expected)) { out = out | otype; } } out } pub fn out_all(&self) -> VType { self.block.out() } pub fn in_types(&self) -> &Vec { &self.input_types } } #[derive(Clone, Debug)] pub struct RStatement { output_to: Option, statement: Box, } impl RStatement { pub fn run(&self, vars: &Vec>) -> VData { let out = self.statement.run(vars); if let Some(v) = self.output_to { *vars[v].lock().unwrap() = out; VDataEnum::Tuple(vec![]).to() } else { out } } pub fn out(&self) -> VType { if self.output_to.is_some() { return VType { types: vec![VSingleType::Tuple(vec![])], }; } self.statement.out() } } #[derive(Clone, Debug)] pub enum RStatementEnum { Value(VData), Tuple(Vec), List(Vec), Variable(usize, VType, bool), // Arc> here, because imagine variable in for loop that is used in a different thread -> we need multiple "same" variables FunctionCall(Arc, Vec), BuiltinFunction(BuiltinFunction, Vec), Block(RBlock), If(RStatement, RStatement, Option), While(RStatement), For(usize, RStatement, RStatement), Switch(RStatement, Vec<(VType, RStatement)>), Match(usize, Vec<(RStatement, RStatement)>), IndexFixed(RStatement, usize), } impl RStatementEnum { pub fn run(&self, vars: &Vec>) -> VData { match self { Self::Value(v) => v.clone(), Self::Tuple(v) => { let mut w = vec![]; for v in v { w.push(v.run(vars)); } VDataEnum::Tuple(w).to() } Self::List(v) => { let mut w = vec![]; let mut out = VType { types: vec![] }; for v in v { let val = v.run(vars); out = out | val.out(); w.push(val); } VDataEnum::List(out, w).to() } Self::Variable(v, _, is_ref) => { if *is_ref { VDataEnum::Reference(vars[*v].clone()).to() } else { vars[*v].lock().unwrap().clone() } } Self::FunctionCall(func, args) => { for (i, input) in func.inputs.iter().enumerate() { *vars[*input].lock().unwrap() = args[i].run(vars); } func.run(vars) } Self::Block(b) => b.run(vars), Self::If(c, t, e) => { if let VDataEnum::Bool(v) = c.run(vars).data { if v { t.run(vars) } else { if let Some(e) = e { e.run(vars) } else { VDataEnum::Tuple(vec![]).to() } } } else { unreachable!() } } Self::While(c) => loop { // While loops blocks can return a bool (false to break from the loop) or a 0-1 length tuple (0-length => continue, 1-length => break with value) match c.run(vars).data { VDataEnum::Bool(v) => { if !v { break VDataEnum::Tuple(vec![]).to(); } } VDataEnum::Tuple(v) if v.len() == 0 => (), VDataEnum::Tuple(v) if v.len() == 1 => break v[0].clone(), _ => unreachable!(), } }, Self::For(v, c, b) => { let c = c.run(vars); let mut vars = vars.clone(); let mut in_loop = |c| { vars[*v] = Arc::new(Mutex::new(c)); b.run(&vars); }; match c.data { VDataEnum::Int(v) => { for i in 0..v { in_loop(VDataEnum::Int(i).to()); } } VDataEnum::String(v) => { for ch in v.chars() { in_loop(VDataEnum::String(ch.to_string()).to()) } } VDataEnum::Tuple(v) | VDataEnum::List(_, v) => { for v in v { in_loop(v) } } _ => unreachable!(), } VDataEnum::Tuple(vec![]).to() } Self::BuiltinFunction(v, args) => v.run(args, vars), Self::Switch(switch_on, cases) => { let switch_on = switch_on.run(vars); let switch_on_type = switch_on.out(); let mut out = VDataEnum::Tuple(vec![]).to(); for (case_type, case_action) in cases.iter() { if switch_on_type.fits_in(case_type).is_empty() { out = case_action.run(vars); break; } } out } Self::Match(match_on, cases) => 'm: { for (case_condition, case_action) in cases { // [t] => Some(t), t => Some(t), [] => None if let Some(v) = match case_condition.run(vars).data { VDataEnum::Tuple(mut tuple) => tuple.pop(), VDataEnum::Bool(v) => if v { Some(VDataEnum::Bool(v).to()) } else { None }, other => Some(other.to()), } { let og = { std::mem::replace(&mut *vars[*match_on].lock().unwrap(), v) }; let res = case_action.run(vars); *vars[*match_on].lock().unwrap() = og; break 'm res; } } VDataEnum::Tuple(vec![]).to() } Self::IndexFixed(st, i) => st.run(vars).get(*i).unwrap(), } } pub fn out(&self) -> VType { match self { Self::Value(v) => v.out(), Self::Tuple(v) => VSingleType::Tuple(v.iter().map(|v| v.out()).collect()).into(), Self::List(v) => VSingleType::List({ let mut types = VType { types: vec![] }; for t in v { types = types | t.out(); } types }) .into(), Self::Variable(_, t, is_ref) => { if *is_ref { VType { types: t .types .iter() .map(|t| VSingleType::Reference(Box::new(t.clone()))) .collect(), } } else { t.clone() } } Self::FunctionCall(f, args) => f.out_vt(&args.iter().map(|v| v.out()).collect()), Self::Block(b) => b.out(), Self::If(_, a, b) => { if let Some(b) = b { a.out() | b.out() } else { a.out() } } Self::While(c) => { let mut output_types = VType { types: vec![] }; for t in c.out().types { match t { VSingleType::Bool => { output_types = output_types | VSingleType::Tuple(vec![]).to(); } VSingleType::Tuple(mut t) => { if !t.is_empty() { if t.len() != 1 { unreachable!("while loop with tuple of length {}>1.", t.len()); } output_types = output_types | t.pop().unwrap(); } } _ => unreachable!( "while loop statement didn't return bool or 0-1 length tuple." ), } } output_types } Self::For(_, _, b) => { // returns the return value from the last iteration or nothing if there was no iteration b.out() | VType { types: vec![VSingleType::Tuple(vec![])], } } Self::BuiltinFunction(f, args) => f.returns(args.iter().map(|rs| rs.out()).collect()), Self::Switch(switch_on, cases) => { let switch_on = switch_on.out().types; let mut might_return_empty = switch_on.is_empty(); let mut out = VType { types: vec![] }; // if nothing is executed for switch_on in switch_on { let switch_on: VType = switch_on.into(); 'search: { for (on_type, case) in cases.iter() { if switch_on.fits_in(&on_type).is_empty() { out = out | case.out(); break 'search; } } might_return_empty = true; } } if might_return_empty { out = out | VSingleType::Tuple(vec![]).to(); } out } Self::Match(_, cases) => { let mut out = VSingleType::Tuple(vec![]).to(); for case in cases { out = out | case.1.out(); } out } Self::IndexFixed(st, i) => st.out().get(*i).unwrap(), } } pub fn to(self) -> RStatement { RStatement { output_to: None, statement: Box::new(self), } } } #[derive(Debug)] pub struct RScript { main: RFunction, vars: usize, } impl RScript { fn new(main: RFunction, vars: usize) -> Result { if main.inputs.len() != 1 { return Err(ToRunnableError::MainWrongInput); } Ok(Self { main, vars }) } pub fn run(&self, args: Vec) -> VData { let mut vars = Vec::with_capacity(self.vars); vars.push(am(VDataEnum::List( VSingleType::String.into(), args.into_iter() .map(|v| VDataEnum::String(v).to()) .collect(), ) .to())); for _i in 1..self.vars { vars.push(am(VDataEnum::Tuple(vec![]).to())); } self.main.run(&vars) } } impl Display for SFunction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "(")?; for (i, (n, t)) in self.inputs.iter().enumerate() { if i != 0 { write!(f, " ")?; } write!(f, "{n} {t}")?; } write!(f, ") {}", self.block)?; Ok(()) } } impl Display for SBlock { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{{")?; for s in self.statements.iter() { writeln!(f, "{s}")?; } write!(f, "}}")?; Ok(()) } } impl Display for VType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for (i, t) in self.types.iter().enumerate() { if i != 0 { write!(f, "/")?; } write!(f, "{t}")?; } Ok(()) } } impl Display for VSingleType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Bool => write!(f, "bool"), Self::Int => write!(f, "int"), Self::Float => write!(f, "float"), Self::String => write!(f, "string"), Self::Tuple(types) => { write!(f, "[")?; for (i, t) in types.iter().enumerate() { if i != 0 { write!(f, " {t}")?; } else { write!(f, "{t}")?; } } write!(f, "]")?; Ok(()) } Self::List(t) => write!(f, "[{t}]"), Self::Function(_) => write!(f, "FUNCTION"), Self::Thread(_) => write!(f, "THREAD"), Self::Reference(r) => write!(f, "&{r}"), } } } impl Display for SStatement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(to) = self.output_to.as_ref() { write!(f, "{} = ", to.as_str())?; } write!(f, "{}", self.statement) } } impl Display for SStatementEnum { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { SStatementEnum::Value(v) => write!(f, "{v}"), SStatementEnum::Tuple(v) => { write!( f, "[{}]", v.iter().map(|v| format!("{} ", v)).collect::() ) } SStatementEnum::List(v) => { write!( f, "[{} ...]", v.iter().map(|v| format!("{} ", v)).collect::() ) } SStatementEnum::Variable(v, is_ref) => { write!(f, "{}{v}", if *is_ref { "&" } else { "" }) } SStatementEnum::FunctionCall(func, args) => { write!(f, "{func}(")?; for (i, arg) in args.iter().enumerate() { if i != 0 { write!(f, " ")?; } write!(f, "{arg}")?; } write!(f, ")") } SStatementEnum::FunctionDefinition(name, func) => { if let Some(name) = name { write!(f, "{name}{func}") } else { write!(f, "{func}") } } SStatementEnum::Block(b) => write!(f, "{b}"), SStatementEnum::If(c, a, b) => { write!(f, "if {c} {a}")?; if let Some(b) = b { write!(f, " else {b}")?; } Ok(()) } SStatementEnum::While(c) => write!(f, "while {c}"), SStatementEnum::For(v, c, b) => write!(f, "for {v} {c} {b}"), SStatementEnum::Switch(switch_on, cases, force) => { writeln!( f, "switch{} {} {{", if *force { "!" } else { "" }, switch_on )?; for (case_type, case_action) in cases.iter() { writeln!(f, "{} {}", case_type, case_action)?; } write!(f, "}}") } SStatementEnum::Match(match_on, cases) => { writeln!(f, "match {match_on} {{")?; for (case_cond, case_action) in cases.iter() { writeln!(f, "{} {}", case_cond, case_action)?; } write!(f, "}}") } SStatementEnum::IndexFixed(st, i) => write!(f, "{st}.{i}"), } } } impl Display for VData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.data) } } impl Display for VDataEnum { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Bool(v) => { if *v { write!(f, "true") } else { write!(f, "false") } } Self::Int(v) => write!(f, "{v}"), Self::Float(v) => write!(f, "{v}"), Self::String(v) => write!(f, "{v}"), Self::Tuple(v) | Self::List(_, v) => { write!(f, "[")?; for (i, v) in v.iter().enumerate() { if i != 0 { write!(f, " {v}")?; } else { write!(f, "{v}")?; } } match self { Self::List(..) => write!(f, "...")?, _ => (), } write!(f, "]")?; Ok(()) } Self::Function(v) => write!(f, "{v}"), Self::Thread(..) => write!(f, "THREAD"), Self::Reference(r) => write!(f, "{}", r.lock().unwrap()), } } } impl Display for RFunction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "({}) {:#?}", self.inputs.len(), self.block) } }