initial code commit, implemented switch statements and if-else syntax.

This commit is contained in:
Dummi26
2023-03-09 13:24:52 +01:00
parent bb2f4b541c
commit 63721b9e26
9 changed files with 1670 additions and 1 deletions

924
src/script/block.rs Normal file
View File

@@ -0,0 +1,924 @@
// 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::{
collections::HashMap,
fmt::Display,
sync::{Arc, Mutex},
time::Duration,
};
use self::to_runnable::ToRunnableError;
use super::value::{VData, VDataEnum, VDataThreadEnum, VSingleType, VType};
// Represents a block of code
#[derive(Debug)]
pub struct SBlock {
pub statements: Vec<SStatement>,
}
impl SBlock {
pub fn new(statements: Vec<SStatement>) -> 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<String>,
pub statement: Box<SStatementEnum>,
}
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),
Variable(String),
FunctionCall(String, Vec<SStatement>),
FunctionDefinition(Option<String>, SFunction),
Block(SBlock),
If(SStatement, SStatement, Option<SStatement>),
While(SStatement),
For(String, SStatement, SStatement),
Switch(SStatement, Vec<(VType, SStatement)>, bool),
// Match(???),
}
impl Into<SStatement> for SStatementEnum {
fn into(self) -> SStatement {
SStatement::new(self)
}
}
// Conversion
type Am<T> = Arc<Mutex<T>>;
fn am<T>(i: T) -> Am<T> {
Arc::new(Mutex::new(i))
}
pub fn to_runnable(f: SFunction) -> Result<RScript, ToRunnableError> {
to_runnable::to_runnable(f)
}
pub mod to_runnable {
use std::{
collections::HashMap,
fmt::{Debug, Display},
sync::Arc,
};
use crate::script::value::{VData, VDataEnum, VSingleType, VType};
use super::{
Am, 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<VSingleType>,
},
InvalidTypeForWhileLoop(VType),
CaseForceButTypeNotCovered(VType),
}
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}"),
}
}
}
// Global, shared between all
struct GInfo {
vars: usize,
}
// Local, used to keep local variables separated
#[derive(Clone)]
struct LInfo {
vars: HashMap<String, (usize, VType)>,
fns: HashMap<String, Arc<RFunction>>,
}
pub fn to_runnable(s: SFunction) -> Result<RScript, ToRunnableError> {
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<usize>,
inputs: &mut Vec<VSingleType>,
out: &mut Vec<(Vec<VSingleType>, 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<RFunction, ToRunnableError> {
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<RBlock, ToRunnableError> {
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<RStatement, ToRunnableError> {
let mut statement = match &*s.statement {
SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()),
SStatementEnum::Variable(v) => {
if let Some(var) = linfo.vars.get(v) {
RStatementEnum::Variable(var.0, var.1.clone())
} 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 {
RStatementEnum::BuiltinFunction(
// TODO: type-checking for builtins
match v.as_str() {
"print" => BuiltinFunction::Print,
"debug" => BuiltinFunction::Debug,
"to_string" => BuiltinFunction::ToString,
"format" => BuiltinFunction::Format,
"run" => BuiltinFunction::Run,
"thread" => BuiltinFunction::Thread,
"await" => BuiltinFunction::Await,
"sleep" => BuiltinFunction::Sleep,
_ => return Err(ToRunnableError::UseOfUndefinedFunction(v.clone())),
},
rargs,
)
}
}
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) => {
let mut ncases = Vec::with_capacity(cases.len());
for case in cases {
ncases.push((case.0.clone(), statement(&case.1, ginfo, linfo)?));
eprintln!("NCASE: {:#?}", ncases.last().unwrap().0);
}
let switch_on = statement(switch_on, ginfo, linfo)?;
let switch_on_out = switch_on.out();
if *force {
for val_type in switch_on_out.types.iter() {
let val_type: VType = val_type.clone().into();
'force: {
for (case_type, _) in ncases.iter() {
if val_type.fits_in(&case_type).is_empty() {
eprintln!("Breaking.");
break 'force;
}
}
return Err(ToRunnableError::CaseForceButTypeNotCovered(val_type));
}
}
}
RStatementEnum::Switch(switch_on, ncases)
}
}
.to();
if let Some(opt) = &s.output_to {
if let Some(var) = linfo.vars.get_mut(opt) {
var.1 = var.1.clone() | statement.out();
statement.output_to = Some(var.0);
} 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<RStatement>,
}
impl RBlock {
pub fn run(&self, vars: &Vec<Am<VData>>) -> 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 {
inputs: Vec<usize>,
input_types: Vec<VType>,
input_output_map: Vec<(Vec<VSingleType>, VType)>,
block: RBlock,
}
impl RFunction {
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
self.block.run(vars)
}
pub fn out(&self, input_types: &Vec<VSingleType>) -> 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_all(&self) -> VType {
self.block.out()
}
pub fn in_types(&self) -> &Vec<VType> {
&self.input_types
}
}
#[derive(Clone, Debug)]
pub struct RStatement {
output_to: Option<usize>,
statement: Box<RStatementEnum>,
}
impl RStatement {
pub fn run(&self, vars: &Vec<Am<VData>>) -> 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),
Variable(usize, VType), // Arc<Mutex<..>> here, because imagine variable in for loop that is used in a different thread -> we need multiple "same" variables
FunctionCall(Arc<RFunction>, Vec<RStatement>),
BuiltinFunction(BuiltinFunction, Vec<RStatement>),
Block(RBlock),
If(RStatement, RStatement, Option<RStatement>),
While(RStatement),
For(usize, RStatement, RStatement),
Switch(RStatement, Vec<(VType, RStatement)>),
}
#[derive(Clone, Debug)]
pub enum BuiltinFunction {
// print
Print,
Debug,
// format
ToString,
Format,
// math and basic operators (not possible, need to be different for each type)
// Add,
// Sub,
// Mul,
// Div,
// Mod,
// functions
Run,
Thread,
Await,
Sleep,
}
impl RStatementEnum {
pub fn run(&self, vars: &Vec<Am<VData>>) -> VData {
match self {
Self::Value(v) => v.clone(),
Self::Variable(v, _) => 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() == 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) => match v {
BuiltinFunction::Print => {
if let VDataEnum::String(arg) = args[0].run(vars).data {
println!("{}", arg);
VDataEnum::Tuple(vec![]).to()
} else {
unreachable!()
}
}
BuiltinFunction::Debug => {
println!("{:?}", args[0].run(vars).data);
VDataEnum::Tuple(vec![]).to()
}
BuiltinFunction::ToString => {
VDataEnum::String(format!("{}", args[0].run(vars).data)).to()
}
BuiltinFunction::Format => todo!("Format function (and validity check!)"),
BuiltinFunction::Run => {
if args.len() >= 1 {
if let VDataEnum::Function(f) = args[0].run(vars).data {
if f.inputs.len() != args.len() - 1 {
unreachable!()
}
for (i, var) in f.inputs.iter().enumerate() {
let val = args[i + 1].run(vars);
*vars[*var].lock().unwrap() = val;
}
f.run(vars)
} else {
unreachable!()
}
} else {
unreachable!()
}
}
BuiltinFunction::Thread => {
if args.len() >= 1 {
if let VDataEnum::Function(f) = args[0].run(vars).data {
if f.inputs.len() != args.len() - 1 {
unreachable!()
}
// to prevent weird stuff from happening, the function args will be stored in different Arc<Mutex<_>>s. This means that the args are different for each thread, while any variables that are captured from outside will be shared.
let mut thread_vars = vars.clone();
let mut run_input_types = vec![];
for (i, var) in f.inputs.iter().enumerate() {
let val = args[i + 1].run(vars);
run_input_types.push(val.out_single());
thread_vars[*var] = Arc::new(Mutex::new(val));
}
let out_type = f.out(&run_input_types);
VDataEnum::Thread(
VDataThreadEnum::Running(std::thread::spawn(move || {
f.run(&thread_vars)
}))
.to(),
out_type,
)
.to()
} else {
unreachable!()
}
} else {
unreachable!()
}
}
BuiltinFunction::Await => {
if args.len() == 1 {
if let VDataEnum::Thread(t, _) = args[0].run(vars).data {
t.get()
} else {
unreachable!()
}
} else {
unreachable!()
}
}
BuiltinFunction::Sleep => {
if args.len() == 1 {
match args[0].run(vars).data {
VDataEnum::Int(v) => std::thread::sleep(Duration::from_secs(v as _)),
VDataEnum::Float(v) => std::thread::sleep(Duration::from_secs_f64(v)),
_ => unreachable!(),
}
VDataEnum::Tuple(vec![]).to()
} else {
unreachable!()
}
}
},
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
}
}
}
pub fn out(&self) -> VType {
match self {
Self::Value(v) => v.out(),
Self::Variable(_, t) => t.clone(),
Self::FunctionCall(f, _) => {
eprintln!("Warn: generalizing a functions return type regardless of the inputs. Type-checker might assume this value can have more types than it really can.");
f.out_all()
}
Self::Block(b) => b.out(),
Self::If(_, a, b) => {
if let Some(b) = b {
a.out() | b.out()
} else {
a.out()
}
}
Self::While(c) => todo!("while loop output type"),
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, _) => match f {
BuiltinFunction::Print | BuiltinFunction::Debug | BuiltinFunction::Sleep => VType {
types: vec![VSingleType::Tuple(vec![])],
},
BuiltinFunction::ToString | BuiltinFunction::Format => VSingleType::String.into(),
BuiltinFunction::Run | BuiltinFunction::Thread | BuiltinFunction::Await => {
VType { types: vec![] } // TODO!
// unreachable!("this has to be implemented somewhere else!")
}
},
Self::Switch(_, cases) => {
let mut out = VSingleType::Tuple(vec![]).into(); // if nothing is executed
for (_, case) in cases.iter() {
out = out | case.out();
}
out
}
}
}
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<Self, ToRunnableError> {
if main.inputs.len() != 1 {
return Err(ToRunnableError::MainWrongInput);
}
Ok(Self { main, vars })
}
pub fn run(&self, args: Vec<String>) -> VData {
let mut vars = Vec::with_capacity(self.vars);
vars.push(am(VDataEnum::List(
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 t in types {
write!(f, "{t}")?;
}
write!(f, "]")?;
Ok(())
}
Self::List(t) => write!(f, "[{t}]"),
Self::Function(args, out) => write!(f, "({args:?}) -> {out}"),
Self::Thread(_) => write!(f, "THREAD"),
}
}
}
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::Variable(v) => write!(f, "{v}"),
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, "}}")
}
}
}
}
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 v in v {
write!(f, "{v}")?;
}
write!(f, "]")?;
Ok(())
}
Self::Function(v) => write!(f, "{v}"),
Self::Thread(..) => write!(f, "THREAD"),
}
}
}
impl Display for RFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}) {:#?}", self.inputs.len(), self.block)
}
}