From 89cc7971ee3f1a2cc8fc7e77d0410a9560cbc286 Mon Sep 17 00:00:00 2001 From: Dummi26 Date: Tue, 25 Apr 2023 19:38:00 +0200 Subject: [PATCH] - revamped fmt methods for VData, VType, and some others => value == parse(value.fmtgi(..)) - updated debug() method to output static_type :: dynamic_type :: value - updated to_string() to output "val" if called on a string (it just uses the fmt methods) - updated format() to only take strings as arguments --- README.md | 18 +- mers/src/libs/mod.rs | 10 +- mers/src/parse/parse.rs | 117 +-- mers/src/script/block.rs | 1303 ------------------------------ mers/src/script/builtins.rs | 167 ++-- mers/src/script/code_parsed.rs | 235 ++++++ mers/src/script/code_runnable.rs | 391 +++++++++ mers/src/script/global_info.rs | 15 + mers/src/script/mod.rs | 5 +- mers/src/script/to_runnable.rs | 629 ++++++++++++++ mers/src/script/val_data.rs | 90 ++- mers/src/script/val_type.rs | 114 ++- mers/src/script/value.rs | 1 - mers/src/tutor/base_functions.rs | 5 + mers/src/tutor/base_types.rs | 3 + mers/src/tutor/mod.rs | 2 +- musicdb_remote.mers | 2 +- thread.mers | 4 +- 18 files changed, 1655 insertions(+), 1456 deletions(-) delete mode 100755 mers/src/script/block.rs create mode 100644 mers/src/script/code_parsed.rs create mode 100644 mers/src/script/code_runnable.rs create mode 100644 mers/src/script/global_info.rs create mode 100644 mers/src/script/to_runnable.rs delete mode 100755 mers/src/script/value.rs diff --git a/README.md b/README.md index 2cb7539..2725b55 100755 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Now let's add a counter variable, read user input and print the status message. counter = 0 while { input = read_line() - println("The counter is currently at {0}. Type + or - to change it.".format(counter)) + println("The counter is currently at {0}. Type + or - to change it.".format(counter.to_string())) } We can then use `eq(a b)` to check if the input is equal to + or -, and then decide to increase or decrease counter: @@ -152,7 +152,7 @@ We can then use `eq(a b)` to check if the input is equal to + or -, and then dec } else if input.eq("-") { counter = counter.sub(1) } else { - println("The counter is currently at {0}. Type + or - to change it.".format(counter)) + println("The counter is currently at {0}. Type + or - to change it.".format(counter.to_string())) } } @@ -164,7 +164,7 @@ mers actually doesn't have an else-if, the if statement is simply parsed as: if input.eq("-") { counter = counter.sub(1) } else { - println("The counter is currently at {0}. Type + or - to change it.".format(counter)) + println("The counter is currently at {0}. Type + or - to change it.".format(counter.to_string())) } } @@ -182,7 +182,7 @@ Let's replace the if statement from before with a match statement! match input { input.eq("+") counter = counter.add(1) input.eq("-") counter = counter.sub(1) - true println("The counter is currently at {0}. Type + or - to change it.".format(counter)) + true println("The counter is currently at {0}. Type + or - to change it.".format(counter.to_string())) } } @@ -203,7 +203,7 @@ Loops will break if the value returned in the current iteration matches: i = i.add(1) i.gt(50) } - println("res: {0}".format(res)) + println("res: {0}".format(res.to_string())) This will increment i until it reaches 51. Because `51.gt(50)` returns `true`, `res` will be set to `true`. @@ -213,7 +213,7 @@ Because `51.gt(50)` returns `true`, `res` will be set to `true`. i = i.add(1) if i.gt(50) i else [] } - println("res: {0}".format(res)) + println("res: {0}".format(res.to_string())) Because a value of type int matches, we now break with "res: 51". For more complicated examples, using `[i]` instead of just `i` is recommended because `[i]` matches even if `i` doesn't. @@ -288,7 +288,7 @@ However, match statements have a superpower: They can change the value of the va x = 10 match x { - x.eq(10) println("x is now {0}".format(x)) + x.eq(10) println("x is now {0}".format(x.to_string())) true println("x was not 10.") } @@ -304,8 +304,8 @@ Using a match statement, this is one way to implement it: strings = ["87" "not a number" "25" "14.5" ...] for x strings { match x { - x.parse_int() println("int: {0} = 10 * {1} + {2}".format(x x.sub(x.mod(10)).div(10) x.mod(10))) - x.parse_float() println("float: {0} = {1} + {2}".format(x x.sub(x.mod(1)) x.mod(1))) + x.parse_int() println("int: {0} = 10 * {1} + {2}".format(x.to_string() x.sub(x.mod(10)).div(10).to_string() x.mod(10).to_string())) + x.parse_float() println("float: {0} = {1} + {2}".format(x.to_string() x.sub(x.mod(1)).to_string() x.mod(1).to_string())) true println("not a number") } } diff --git a/mers/src/libs/mod.rs b/mers/src/libs/mod.rs index 1132986..518799b 100755 --- a/mers/src/libs/mod.rs +++ b/mers/src/libs/mod.rs @@ -121,7 +121,10 @@ impl Lib { fn_signature.next(); } else { loop { - let mut t = parse::parse_type_adv(&mut fn_signature, true).unwrap(); + let mut t = match parse::parse_type_adv(&mut fn_signature, true) { + Ok(v) => v, + Err(e) => panic!("{e}"), + }; t.0.enum_variants(enum_variants); fn_in.push(t.0); if t.1 { @@ -129,7 +132,10 @@ impl Lib { } } } - let mut fn_out = parse::parse_type(&mut fn_signature).unwrap(); + let mut fn_out = match parse::parse_type(&mut fn_signature) { + Ok(v) => v, + Err(e) => panic!("{e}"), + }; fn_out.enum_variants(enum_variants); eprintln!("Registering function \"{name}\" with args \"{}\" and return type \"{fn_out}\"", &fn_in.iter().fold(String::new(), |mut s, v| { s.push_str(format!(" {}", v).as_str()); s }).trim_start_matches(' ')); registered_fns.push((name.to_string(), fn_in, fn_out)); diff --git a/mers/src/parse/parse.rs b/mers/src/parse/parse.rs index e70f916..60c70cf 100755 --- a/mers/src/parse/parse.rs +++ b/mers/src/parse/parse.rs @@ -3,11 +3,10 @@ use std::{process::Command, sync::Arc}; use crate::{ libs, script::{ - block::{ - to_runnable::ToRunnableError, - to_runnable::{self, GInfo}, - RScript, SBlock, SFunction, SStatement, SStatementEnum, - }, + code_parsed::*, + code_runnable::RScript, + global_info::GSInfo, + to_runnable::{self, GInfo, ToRunnableError}, val_data::VDataEnum, val_type::{VSingleType, VType}, }, @@ -15,7 +14,6 @@ use crate::{ use super::file::File; -#[derive(Debug)] pub enum ScriptError { CannotFindPathForLibrary(CannotFindPathForLibrary), ParseError(ParseError), @@ -78,18 +76,9 @@ pub fn parse(file: &mut File) -> Result { let mut ginfo = GInfo::default(); let libs = parse_step_lib_paths(file)?; let func = parse_step_interpret(file)?; - ginfo.libs = Arc::new(parse_step_libs_load(libs, &mut ginfo)?); + ginfo.libs = parse_step_libs_load(libs, &mut ginfo)?; - eprintln!(); - #[cfg(debug_assertions)] - eprintln!("Parsed: {func}"); - #[cfg(debug_assertions)] - eprintln!("Parsed: {func:#?}"); - - let run = parse_step_compile(func, &mut ginfo)?; - - #[cfg(debug_assertions)] - eprintln!("Runnable: {run:#?}"); + let run = parse_step_compile(func, ginfo)?; Ok(run) } @@ -130,7 +119,7 @@ pub fn parse_step_interpret(file: &mut File) -> Result { Ok(SFunction::new( vec![( "args".to_string(), - VSingleType::List(VSingleType::String.into()).into(), + VSingleType::List(VSingleType::String.into()).to(), )], parse_block_advanced(file, Some(false), true, true, false)?, )) @@ -163,10 +152,7 @@ pub fn parse_step_libs_load( Ok(libs) } -pub fn parse_step_compile( - main_func: SFunction, - ginfo: &mut GInfo, -) -> Result { +pub fn parse_step_compile(main_func: SFunction, ginfo: GInfo) -> Result { to_runnable::to_runnable(main_func, ginfo) } @@ -181,7 +167,6 @@ impl<'a> std::fmt::Display for ParseErrorWithFile<'a> { self.0.fmt_custom(f, Some(self.1)) } } -#[derive(Debug)] pub struct ParseError { err: ParseErrors, // the location of the error @@ -191,6 +176,7 @@ pub struct ParseError { String, Option<(super::file::FilePosition, Option)>, )>, + info: Option, } impl ParseError { pub fn fmt_custom( @@ -198,7 +184,8 @@ impl ParseError { f: &mut std::fmt::Formatter<'_>, file: Option<&super::file::File>, ) -> std::fmt::Result { - writeln!(f, "{}", self.err)?; + self.err.fmtgs(f, self.info.as_ref(), file)?; + writeln!(f); if let Some(location_end) = self.location_end { writeln!(f, " from {} to {}", self.location, location_end)?; if let Some(file) = file { @@ -238,7 +225,6 @@ impl std::fmt::Display for ParseError { self.fmt_custom(f, None) } } -#[derive(Debug)] pub enum ParseErrors { StatementCannotStartWith(char), FoundClosingRoundBracketInSingleStatementBlockBeforeAnyStatement, @@ -254,8 +240,13 @@ pub enum ParseErrors { CannotWrapWithThisStatement(SStatementEnum), ErrorParsingFunctionArgs(Box), } -impl std::fmt::Display for ParseErrors { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl ParseErrors { + fn fmtgs( + &self, + f: &mut std::fmt::Formatter, + info: Option<&GSInfo>, + file: Option<&super::file::File>, + ) -> std::fmt::Result { match self { Self::StatementCannotStartWith(ch) => { write!(f, "statements cannot start with the {ch} character.",) @@ -281,13 +272,18 @@ impl std::fmt::Display for ParseErrors { Self::FoundEofInsteadOfType => write!(f, "expected type, found EOF instead."), Self::InvalidType(name) => write!(f, "\"{name}\" is not a type."), Self::CannotUseFixedIndexingWithThisType(t) => { - write!(f, "cannot use fixed-indexing with type {t}.") + write!(f, "cannot use fixed-indexing with type ")?; + t.fmtgs(f, info)?; + write!(f, ".") } Self::CannotWrapWithThisStatement(s) => { - write!(f, "cannot wrap with this kind of statement: {s}.") + write!(f, "cannot wrap with this kind of statement: ")?; + s.fmtgs(f, info)?; + write!(f, ".") } Self::ErrorParsingFunctionArgs(parse_error) => { - write!(f, "error parsing function args: {}", parse_error.err) + write!(f, "error parsing function args: ")?; + parse_error.fmt_custom(f, file) } } } @@ -329,6 +325,7 @@ fn parse_block_advanced( location: err_start_of_this_statement, location_end: Some(*file.get_pos()), context: vec![], + info: None, }); } else { file.next(); @@ -341,7 +338,8 @@ fn parse_block_advanced( err: ParseErrors::FoundClosingCurlyBracketInSingleStatementBlockBeforeAnyStatement, location: err_start_of_this_statement, location_end: Some(*file.get_pos()), - context: vec![] + context: vec![], + info: None, }); } else { file.next(); @@ -357,6 +355,7 @@ fn parse_block_advanced( location: err_start_of_this_statement, location_end: Some(*file.get_pos()), context: vec![], + info: None, }) } _ => (), @@ -387,7 +386,7 @@ fn parse_statement_adv( file.skip_whitespaces(); let err_start_of_statement = *file.get_pos(); let out = match file.peek() { - Some('{') => Some(SStatementEnum::Block(parse_block(file)?).into()), + Some('{') => Some(SStatementEnum::Block(parse_block(file)?).to()), Some('[') => { file.next(); let mut v = vec![]; @@ -409,9 +408,9 @@ fn parse_statement_adv( v.push(parse_statement(file)?); } Some(if list { - SStatementEnum::List(v).into() + SStatementEnum::List(v).to() } else { - SStatementEnum::Tuple(v).into() + SStatementEnum::Tuple(v).to() }) } Some('"') => { @@ -441,11 +440,12 @@ fn parse_statement_adv( location: err_start_of_statement, location_end: Some(*file.get_pos()), context: vec![], + info: None, }) } } } - Some(SStatementEnum::Value(VDataEnum::String(buf).to()).into()) + Some(SStatementEnum::Value(VDataEnum::String(buf).to()).to()) } _ => None, }; @@ -509,6 +509,7 @@ fn parse_statement_adv( location: *file.get_pos(), location_end: None, context: vec![], + info: None, }); } file.skip_whitespaces(); @@ -549,7 +550,7 @@ fn parse_statement_adv( Some(fn_name.trim().to_string()), func, ) - .into(); + .to(); } "if" => { // TODO: Else @@ -562,7 +563,7 @@ fn parse_statement_adv( while let Some('e' | 'l' | 's') = file.next() {} then_else = Some(parse_statement(file)?); } - break SStatementEnum::If(condition, then, then_else).into(); + break SStatementEnum::If(condition, then, then_else).to(); } "for" => { break SStatementEnum::For( @@ -584,10 +585,10 @@ fn parse_statement_adv( parse_statement(file)?, parse_statement(file)?, ) - .into() + .to() } "while" => { - break SStatementEnum::While(parse_statement(file)?).into(); + break SStatementEnum::Loop(parse_statement(file)?).to(); } "switch" | "switch!" => { let force = start.ends_with("!"); @@ -613,7 +614,7 @@ fn parse_statement_adv( } cases.push((parse_type(file)?, parse_statement(file)?)); } - break SStatementEnum::Switch(switch_on_what, cases, force).into(); + break SStatementEnum::Switch(switch_on_what, cases, force).to(); } "match" => { let mut match_what = String::new(); @@ -638,10 +639,10 @@ fn parse_statement_adv( } cases.push((parse_statement(file)?, parse_statement(file)?)); } - break SStatementEnum::Match(match_what, cases).into(); + break SStatementEnum::Match(match_what, cases).to(); } - "true" => break SStatementEnum::Value(VDataEnum::Bool(true).to()).into(), - "false" => break SStatementEnum::Value(VDataEnum::Bool(false).to()).into(), + "true" => break SStatementEnum::Value(VDataEnum::Bool(true).to()).to(), + "false" => break SStatementEnum::Value(VDataEnum::Bool(false).to()).to(), _ => { // int, float, var break { @@ -658,22 +659,21 @@ fn parse_statement_adv( pot_float.push(ch); } if let Ok(v) = format!("{start}.{pot_float}").parse() { - SStatementEnum::Value(VDataEnum::Float(v).to()).into() + SStatementEnum::Value(VDataEnum::Float(v).to()).to() } else { file.set_pos(pos); - SStatementEnum::Value(VDataEnum::Int(v).to()).into() + SStatementEnum::Value(VDataEnum::Int(v).to()).to() } } else { - SStatementEnum::Value(VDataEnum::Int(v).to()).into() + SStatementEnum::Value(VDataEnum::Int(v).to()).to() } // } else if let Ok(v) = start.parse() { - // SStatementEnum::Value(VDataEnum::Float(v).to()).into() + // SStatementEnum::Value(VDataEnum::Float(v).to()).to() } else { if start.starts_with('&') { - SStatementEnum::Variable(start[1..].to_string(), true) - .into() + SStatementEnum::Variable(start[1..].to_string(), true).to() } else { - SStatementEnum::Variable(start.to_string(), false).into() + SStatementEnum::Variable(start.to_string(), false).to() } } }; @@ -688,7 +688,7 @@ fn parse_statement_adv( None, parse_function(file, Some(err_start_of_statement))?, ) - .into(); + .to(); } else { break SStatementEnum::FunctionCall( name.to_string(), @@ -701,11 +701,12 @@ fn parse_statement_adv( location: err_start_of_statement, location_end: Some(*file.get_pos()), context: vec![], + info: None, }); } }, ) - .into(); + .to(); } } Some(ch) => start.push(ch), @@ -715,6 +716,7 @@ fn parse_statement_adv( location: err_start_of_statement, location_end: Some(*file.get_pos()), context: vec![], + info: None, }) } } @@ -739,10 +741,10 @@ fn parse_statement_adv( out = match *wrapper.statement { SStatementEnum::FunctionCall(func, args) => { let args = [out].into_iter().chain(args.into_iter()).collect(); - SStatementEnum::FunctionCall(func, args).into() + SStatementEnum::FunctionCall(func, args).to() } SStatementEnum::Value(vd) => match vd.data { - VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, i as _).into(), + VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, i as _).to(), _ => { let mut context = vec![]; if chain_length > 0 { @@ -768,6 +770,7 @@ fn parse_statement_adv( location: err_start_of_wrapper, location_end: Some(err_end_of_wrapper), context, + info: None, }); } }, @@ -796,6 +799,7 @@ fn parse_statement_adv( location: err_start_of_wrapper, location_end: Some(err_end_of_wrapper), context, + info: None, }); } }; @@ -838,6 +842,7 @@ fn parse_function( } else { (format!("not a real fn definition"), None) }], + info: None, }) } } @@ -1022,6 +1027,7 @@ fn parse_single_type_adv( location: err_start_of_single_type, location_end: Some(*file.get_pos()), context: vec![], + info: None, }) } } @@ -1051,6 +1057,7 @@ fn parse_single_type_adv( location: err_start_of_single_type, location_end: Some(*file.get_pos()), context: vec![], + info: None, }); } } @@ -1066,6 +1073,7 @@ fn parse_single_type_adv( location: err_start_of_single_type, location_end: Some(*file.get_pos()), context: vec![], + info: None, }); } } @@ -1076,6 +1084,7 @@ fn parse_single_type_adv( location: err_start_of_single_type, location_end: Some(*file.get_pos()), context: vec![], + info: None, }) } }, diff --git a/mers/src/script/block.rs b/mers/src/script/block.rs deleted file mode 100755 index a7236ed..0000000 --- a/mers/src/script/block.rs +++ /dev/null @@ -1,1303 +0,0 @@ -// 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. -// There currently are no T-Types, but we might need them in the future. - -use std::{ - fmt::Display, - sync::{Arc, Mutex}, -}; - -use crate::libs; - -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<(String, usize)>, - pub statement: Box, - pub force_output_type: Option, -} -impl SStatement { - pub fn new(statement: SStatementEnum) -> Self { - Self { - output_to: None, - statement: Box::new(statement), - force_output_type: None, - } - } - pub fn output_to(mut self, var: String, derefs: usize) -> Self { - self.output_to = Some((var, derefs)); - self - } - // forces the statement's output to fit in a certain type. - pub fn force_output_type(mut self, force_output_type: Option) -> Self { - self.force_output_type = force_output_type; - 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), - EnumVariant(String, SStatement), -} -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 mod to_runnable { - use std::{ - collections::HashMap, - fmt::{Debug, Display}, - sync::Arc, - }; - - use crate::{ - libs, - script::{ - builtins, - 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), - CannotDeclareVariableWithDereference(String), - CannotDereferenceTypeNTimes(VType, usize, VType), - FunctionWrongArgCount(String, usize, usize), - InvalidType { - expected: VType, - found: VType, - problematic: VType, - }, - CaseForceButTypeNotCovered(VType), - MatchConditionInvalidReturn(VType), - NotIndexableFixed(VType, usize), - WrongInputsForBuiltinFunction(BuiltinFunction, String, Vec), - WrongArgsForLibFunction(String, Vec), - ForLoopContainerHasNoInnerTypes, - StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA( - VType, - VType, - 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::CannotDeclareVariableWithDereference(v) => write!(f, "Cannot declare a variable and dereference it (variable '{v}')."), - Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => write!(f, - "Cannot dereference type {og_type} {derefs_wanted} times (stopped at {last_valid_type})." - ), - 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::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}."), - Self::WrongInputsForBuiltinFunction(_builtin, builtin_name, args) => { - write!(f, "Wrong arguments for builtin function {}:", builtin_name)?; - for arg in args { - write!(f, " {arg}")?; - } - write!(f, ".") - } - Self::WrongArgsForLibFunction(name, args) => { - write!(f, "Wrong arguments for library function {}:", name)?; - for arg in args { - write!(f, " {arg}")?; - } - write!(f, ".") - } - Self::ForLoopContainerHasNoInnerTypes => { - write!(f, "For loop: container had no inner types, cannot iterate.") - } - Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => write!(f, - "the statement requires its output type to be {required}, but its real output type is {real}, which doesn not fit in the required type because of the problematic types {problematic}." - ), - } - } - } - - // Global, shared between all - pub struct GInfo { - vars: usize, - pub libs: Arc>, - pub lib_fns: HashMap, - pub enum_variants: HashMap, - } - impl Default for GInfo { - fn default() -> Self { - Self { - vars: 0, - libs: Arc::new(vec![]), - lib_fns: HashMap::new(), - enum_variants: Self::default_enum_variants(), - } - } - } - impl GInfo { - pub fn default_enum_variants() -> HashMap { - builtins::EVS - .iter() - .enumerate() - .map(|(i, v)| (v.to_string(), i)) - .collect() - } - pub fn new(libs: Arc>, enum_variants: HashMap) -> Self { - let mut lib_fns = HashMap::new(); - for (libid, lib) in libs.iter().enumerate() { - for (fnid, (name, ..)) in lib.registered_fns.iter().enumerate() { - lib_fns.insert(name.to_string(), (libid, fnid)); - } - } - Self { - vars: 0, - libs, - lib_fns, - enum_variants, - } - } - } - // Local, used to keep local variables separated - #[derive(Clone)] - struct LInfo { - vars: HashMap, - fns: HashMap>, - } - - pub fn to_runnable(s: SFunction, ginfo: &mut GInfo) -> Result { - if s.inputs.len() != 1 || s.inputs[0].0 != "args" { - return Err(ToRunnableError::MainWrongInput); - } - assert_eq!( - s.inputs[0].1, - VType { - types: vec![VSingleType::List(VType { - types: vec![VSingleType::String], - })], - } - ); - let func = function( - &s, - ginfo, - LInfo { - vars: HashMap::new(), - fns: HashMap::new(), - }, - )?; - Ok(RScript::new(func, ginfo.vars, ginfo.libs.clone())?) - } - - // 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 stypes(t: &mut VType, ginfo: &mut GInfo) { - for t in &mut t.types { - stype(t, ginfo); - } - } - fn stype(t: &mut VSingleType, ginfo: &mut GInfo) { - match t { - VSingleType::Tuple(v) => { - for t in v { - stypes(t, ginfo); - } - } - VSingleType::EnumVariantS(e, v) => { - *t = VSingleType::EnumVariant( - { - if let Some(v) = ginfo.enum_variants.get(e) { - *v - } else { - let v = ginfo.enum_variants.len(); - ginfo.enum_variants.insert(e.clone(), v); - v - } - }, - { - stypes(v, ginfo); - v.clone() - }, - ) - } - _ => (), - } - } - 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, { - let mut v = var.1.clone(); stypes(&mut v, ginfo); v }, *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: VType { types: out }, - }); - } - } - RStatementEnum::FunctionCall(func.clone(), rargs) - } else { - // TODO: type-checking for builtins - if let Some(builtin) = BuiltinFunction::get(v) { - let arg_types = rargs.iter().map(|v| v.out()).collect(); - if builtin.can_take(&arg_types) { - RStatementEnum::BuiltinFunction(builtin, rargs) - } else { - return Err(ToRunnableError::WrongInputsForBuiltinFunction(builtin, v.to_string(), arg_types)); - } - } else { - // LIBRARY FUNCTION? - if let Some((libid, fnid)) = ginfo.lib_fns.get(v) { - let (_name, fn_in, fn_out) = &ginfo.libs[*libid].registered_fns[*fnid]; - if fn_in.len() == rargs.len() && fn_in.iter().zip(rargs.iter()).all(|(fn_in, arg)| arg.out().fits_in(fn_in).is_empty()) { - RStatementEnum::LibFunction(*libid, *fnid, rargs, fn_out.clone()) - } else { - // TODO! better error here - return Err(if fn_in.len() == rargs.len() { - ToRunnableError::WrongArgsForLibFunction(v.to_string(), rargs.iter().map(|v| v.out()).collect()) - } else { - ToRunnableError::FunctionWrongArgCount(v.to_string(), fn_in.len(), rargs.len()) - }); - } - } 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: VType { types: out }, - }); - } - }, - statement(&t, ginfo, linfo)?, - match e { - Some(v) => Some(statement(&v, ginfo, linfo)?), - None => None, - }, - ), - SStatementEnum::While(c) => RStatementEnum::While( - statement(&c, ginfo, linfo)? - ), - SStatementEnum::For(v, c, b) => { - let mut linfo = linfo.clone(); - let container = statement(&c, ginfo, &mut linfo)?; - let inner = container.out().inner_types(); - if inner.types.is_empty() { - return Err(ToRunnableError::ForLoopContainerHasNoInnerTypes); - } - linfo - .vars - .insert(v.clone(), (ginfo.vars, inner)); - 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 { - let case0 = { let mut v = case.0.clone(); stypes(&mut v, ginfo); v }; - linfo.vars.get_mut(switch_on).unwrap().1 = case0.clone(); - ncases.push((case0, 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 { - let mut ct = case_type.clone(); - stypes(&mut ct, ginfo); - if val_type.fits_in(&ct).is_empty() { - break 'force; - } - } - types_not_covered_req_error = true; - types_not_covered = types_not_covered | { - let mut v = val_type; - fn make_readable(v: &mut VType, ginfo: &GInfo) { - for t in v.types.iter_mut() { - match t { - VSingleType::EnumVariant(i, v) => { - let mut v = v.clone(); - make_readable(&mut v, ginfo); - *t = VSingleType::EnumVariantS(ginfo.enum_variants.iter().find_map(|(st, us)| if *us == *i { Some(st.clone()) } else { None }).unwrap(), v); - }, - VSingleType::EnumVariantS(_, v) => make_readable(v, ginfo), - VSingleType::Tuple(v) => for t in v.iter_mut() { - make_readable(t, ginfo) - } - VSingleType::List(t) => make_readable(t, ginfo), - VSingleType::Reference(v) => { - let mut v = v.clone().to(); - make_readable(&mut v, ginfo); - assert_eq!(v.types.len(), 1); - *t = VSingleType::Reference(Box::new(v.types.remove(0))); - } - VSingleType::Bool | VSingleType::Int | VSingleType::Float | VSingleType::String | VSingleType::Function(..) | VSingleType::Thread(..) => (), - } - } - } - make_readable(&mut v, &ginfo); - v - }; - } - } - 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)); - } - } - SStatementEnum::EnumVariant(variant, s) => RStatementEnum::EnumVariant({ - if let Some(v) = ginfo.enum_variants.get(variant) { - *v - } else { - let v = ginfo.enum_variants.len(); - ginfo.enum_variants.insert(variant.clone(), v); - v - } - }, statement(s, ginfo, linfo)?), - } - .to(); - // if force_output_type is set, verify that the real output type actually fits in the forced one. - if let Some(force_opt) = &s.force_output_type { - let real_output_type = statement.out(); - let problematic_types = real_output_type.fits_in(force_opt); - if problematic_types.is_empty() { - statement.force_output_type = Some(force_opt.clone()); - } else { - return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types })); - } - } - if let Some((opt, derefs)) = &s.output_to { - if let Some((var_id, var_out)) = linfo.vars.get(opt) { - let out = statement.out(); - let mut var_derefd = var_out.clone(); - for _ in 0..*derefs { - var_derefd = if let Some(v) = var_derefd.dereference() { - v - } else { - return Err(ToRunnableError::CannotDereferenceTypeNTimes( - var_out.clone(), - *derefs, - var_derefd, - )); - } - } - let inv_types = out.fits_in(&var_derefd); - 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."); - if *derefs != 0 { - return Err(ToRunnableError::CannotDeclareVariableWithDereference( - opt.clone(), - )); - } - linfo.vars.insert(opt.clone(), (ginfo.vars, out)); - statement.output_to = Some((ginfo.vars, 0)); - ginfo.vars += 1; - } else { - // mutate existing variable - statement.output_to = Some((*var_id, *derefs)); - } - } else { - let mut out = statement.out(); - for _ in 0..*derefs { - out = if let Some(v) = out.dereference() { - v - } else { - return Err(ToRunnableError::CannotDereferenceTypeNTimes( - statement.out(), - *derefs, - out, - )); - } - } - linfo.vars.insert(opt.clone(), (ginfo.vars, out)); - statement.output_to = Some((ginfo.vars, *derefs)); - ginfo.vars += 1; - } - } - Ok(statement) - } -} - -// Runnable - -#[derive(Clone, Debug)] -pub struct RBlock { - statements: Vec, -} -impl RBlock { - pub fn run(&self, vars: &Vec>, libs: &Arc>) -> VData { - let mut last = None; - for statement in &self.statements { - last = Some(statement.run(vars, libs)); - } - 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>, libs: &Arc>) -> VData { - self.block.run(vars, libs) - } - 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<(usize, usize)>, - statement: Box, - force_output_type: Option, -} -impl RStatement { - pub fn run(&self, vars: &Vec>, libs: &Arc>) -> VData { - let out = self.statement.run(vars, libs); - if let Some((v, derefs)) = self.output_to { - let mut val = vars[v].clone(); - for _ in 0..derefs { - let v = if let VDataEnum::Reference(v) = &val.lock().unwrap().data { - v.clone() - } else { - unreachable!("dereferencing something that isn't a reference in assignment") - }; - val = v; - } - *val.lock().unwrap() = out; - VDataEnum::Tuple(vec![]).to() - } else { - out - } - } - pub fn out(&self) -> VType { - // `a = b` evaluates to [] - if self.output_to.is_some() { - return VType { - types: vec![VSingleType::Tuple(vec![])], - }; - } - if let Some(t) = &self.force_output_type { - return t.clone(); - } - 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), - LibFunction(usize, usize, Vec, VType), - 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), - EnumVariant(usize, RStatement), -} -impl RStatementEnum { - pub fn run(&self, vars: &Vec>, libs: &Arc>) -> VData { - match self { - Self::Value(v) => v.clone(), - Self::Tuple(v) => { - let mut w = vec![]; - for v in v { - w.push(v.run(vars, libs)); - } - 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, libs); - 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, libs); - } - func.run(vars, libs) - } - Self::BuiltinFunction(v, args) => v.run(args, vars, libs), - Self::LibFunction(libid, fnid, args, _) => { - libs[*libid].run_fn(*fnid, &args.iter().map(|arg| arg.run(vars, libs)).collect()) - } - Self::Block(b) => b.run(vars, libs), - Self::If(c, t, e) => { - if let VDataEnum::Bool(v) = c.run(vars, libs).data { - if v { - t.run(vars, libs) - } else { - if let Some(e) = e { - e.run(vars, libs) - } else { - VDataEnum::Tuple(vec![]).to() - } - } - } else { - unreachable!() - } - } - Self::While(c) => loop { - // While loops will break if the value matches. - if let Some(break_val) = c.run(vars, libs).data.matches() { - break break_val; - } - }, - Self::For(v, c, b) => { - // matching values also break with value from a for loop. - let c = c.run(vars, libs); - let mut vars = vars.clone(); - let in_loop = |vars: &mut Vec>>, c| { - vars[*v] = Arc::new(Mutex::new(c)); - b.run(&vars, libs) - }; - - let mut oval = VDataEnum::Tuple(vec![]).to(); - match c.data { - VDataEnum::Int(v) => { - for i in 0..v { - if let Some(v) = - in_loop(&mut vars, VDataEnum::Int(i).to()).data.matches() - { - oval = v; - break; - } - } - } - VDataEnum::String(v) => { - for ch in v.chars() { - if let Some(v) = - in_loop(&mut vars, VDataEnum::String(ch.to_string()).to()) - .data - .matches() - { - oval = v; - break; - } - } - } - VDataEnum::Tuple(v) | VDataEnum::List(_, v) => { - for v in v { - if let Some(v) = in_loop(&mut vars, v).data.matches() { - oval = v; - break; - } - } - } - VDataEnum::Function(f) => loop { - if let Some(v) = f.run(&vars, libs).data.matches() { - if let Some(v) = in_loop(&mut vars, v).data.matches() { - oval = v; - break; - } - } else { - break; - } - }, - _ => unreachable!(), - } - oval - } - Self::Switch(switch_on, cases) => { - let switch_on = switch_on.run(vars, libs); - 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, libs); - break; - } - } - out - } - Self::Match(match_on, cases) => 'm: { - for (case_condition, case_action) in cases { - // [t] => Some(t), t => Some(t), [] | false => None - if let Some(v) = case_condition.run(vars, libs).data.matches() { - let og = { std::mem::replace(&mut *vars[*match_on].lock().unwrap(), v) }; - let res = case_action.run(vars, libs); - *vars[*match_on].lock().unwrap() = og; - break 'm res; - } - } - VDataEnum::Tuple(vec![]).to() - } - Self::IndexFixed(st, i) => st.run(vars, libs).get(*i).unwrap(), - Self::EnumVariant(e, v) => VDataEnum::EnumVariant(*e, Box::new(v.run(vars, libs))).to(), - } - } - 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::LibFunction(.., out) => out.clone(), - Self::Block(b) => b.out(), - Self::If(_, a, b) => { - if let Some(b) = b { - a.out() | b.out() - } else { - a.out() | VSingleType::Tuple(vec![]).to() - } - } - Self::While(c) => c.out().matches().1, - Self::For(_, _, b) => VSingleType::Tuple(vec![]).to() | b.out().matches().1, - 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 = switch_on.to(); - '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(), - Self::EnumVariant(e, v) => VSingleType::EnumVariant(*e, v.out()).to(), - } - } - pub fn to(self) -> RStatement { - RStatement { - output_to: None, - statement: Box::new(self), - force_output_type: None, - } - } -} - -#[derive(Debug)] -pub struct RScript { - main: RFunction, - vars: usize, - libs: Arc>, -} -impl RScript { - fn new( - main: RFunction, - vars: usize, - libs: Arc>, - ) -> Result { - if main.inputs.len() != 1 { - return Err(ToRunnableError::MainWrongInput); - } - Ok(Self { main, vars, libs }) - } - 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, &self.libs) - } -} - -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(t) => { - write!(f, "fn(")?; - for (t, o) in t { - for t in t { - write!(f, "{t} ")?; - } - write!(f, "{o}")?; - } - write!(f, ")")?; - Ok(()) - } - Self::Thread(_) => write!(f, "THREAD"), - Self::Reference(r) => write!(f, "&{r}"), - Self::EnumVariant(v, t) => write!(f, "{v}({t})"), - Self::EnumVariantS(v, t) => write!(f, "{v}({t})"), - } - } -} -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, "{}{} = ", "*".repeat(to.1), to.0.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}"), - SStatementEnum::EnumVariant(e, s) => write!(f, "{e}: {s}"), - } - } -} -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()), - Self::EnumVariant(v, d) => write!(f, "{v}({d})"), - } - } -} -impl Display for RFunction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "({}) {:#?}", self.inputs.len(), self.block) - } -} diff --git a/mers/src/script/builtins.rs b/mers/src/script/builtins.rs index 84db3a6..0a0823f 100755 --- a/mers/src/script/builtins.rs +++ b/mers/src/script/builtins.rs @@ -7,7 +7,8 @@ use std::{ use crate::libs; use super::{ - block::RStatement, + code_runnable::RStatement, + global_info::GSInfo, val_data::{thread::VDataThreadEnum, VData, VDataEnum}, val_type::{VSingleType, VType}, }; @@ -223,11 +224,10 @@ impl BuiltinFunction { Self::Debug => true, Self::ToString => true, Self::Format => { - if let Some(format_string) = input.first() { - format_string.fits_in(&VSingleType::String.to()).is_empty() - } else { - false - } + !input.is_empty() + && input + .iter() + .all(|v| v.fits_in(&VSingleType::String.to()).is_empty()) } Self::StdinReadLine => input.is_empty(), Self::ParseInt | Self::ParseFloat => { @@ -702,10 +702,10 @@ impl BuiltinFunction { &self, args: &Vec, vars: &Vec>>, - libs: &Arc>, + info: &GSInfo, ) -> VData { match self { - Self::Assume1 => match args[0].run(vars, libs).data { + Self::Assume1 => match args[0].run(vars, info).data { VDataEnum::Tuple(mut v) => { if let Some(v) = v.pop() { v @@ -713,7 +713,7 @@ impl BuiltinFunction { panic!( "ASSUMPTION FAILED: assume1 :: {}", if args.len() > 1 { - if let VDataEnum::String(v) = args[1].run(vars, libs).data { + if let VDataEnum::String(v) = args[1].run(vars, info).data { v } else { String::new() @@ -727,13 +727,13 @@ impl BuiltinFunction { v => v.to(), }, Self::AssumeNoEnum => { - let data = args[0].run(vars, libs); + let data = args[0].run(vars, info); match data.data { VDataEnum::EnumVariant(..) => panic!( "ASSUMPTION FAILED: assume_no_enum :: found {} :: {}", - data, + data.gsi(info.clone()), if args.len() > 1 { - if let VDataEnum::String(v) = args[1].run(vars, libs).data { + if let VDataEnum::String(v) = args[1].run(vars, info).data { v } else { String::new() @@ -745,13 +745,13 @@ impl BuiltinFunction { d => d.to(), } } - Self::NoEnum => args[0].run(vars, libs).noenum(), - Self::Matches => match args[0].run(vars, libs).data.matches() { + Self::NoEnum => args[0].run(vars, info).noenum(), + Self::Matches => match args[0].run(vars, info).data.matches() { Some(v) => VDataEnum::Tuple(vec![v]).to(), None => VDataEnum::Tuple(vec![]).to(), }, BuiltinFunction::Print => { - if let VDataEnum::String(arg) = args[0].run(vars, libs).data { + if let VDataEnum::String(arg) = args[0].run(vars, info).data { print!("{}", arg); VDataEnum::Tuple(vec![]).to() } else { @@ -759,7 +759,7 @@ impl BuiltinFunction { } } BuiltinFunction::Println => { - if let VDataEnum::String(arg) = args[0].run(vars, libs).data { + if let VDataEnum::String(arg) = args[0].run(vars, info).data { println!("{}", arg); VDataEnum::Tuple(vec![]).to() } else { @@ -767,7 +767,13 @@ impl BuiltinFunction { } } BuiltinFunction::Debug => { - println!("{:#?}", args[0].run(vars, libs).data); + let val = args[0].run(vars, info); + println!( + "{} :: {} :: {}", + args[0].out().gsi(info.clone()), + val.out().gsi(info.clone()), + val.gsi(info.clone()) + ); VDataEnum::Tuple(vec![]).to() } Self::StdinReadLine => { @@ -776,14 +782,21 @@ impl BuiltinFunction { VDataEnum::String(line.trim_end_matches(['\n', '\r']).to_string()).to() } BuiltinFunction::ToString => { - VDataEnum::String(format!("{}", args[0].run(vars, libs).data)).to() + VDataEnum::String(args[0].run(vars, info).gsi(info.clone()).to_string()).to() } BuiltinFunction::Format => { - if let VDataEnum::String(mut text) = args.first().unwrap().run(vars, libs).data { + if let VDataEnum::String(mut text) = args.first().unwrap().run(vars, info).data { for (i, arg) in args.iter().skip(1).enumerate() { text = text.replace( &format!("{{{i}}}"), - &format!("{}", arg.run(vars, libs).data), + &format!( + "{}", + if let VDataEnum::String(v) = arg.run(vars, info).data { + v + } else { + unreachable!() + } + ), ); } VDataEnum::String(text).to() @@ -793,7 +806,7 @@ impl BuiltinFunction { } BuiltinFunction::ParseInt => { if args.len() == 1 { - if let VDataEnum::String(s) = args[0].run(vars, libs).data { + if let VDataEnum::String(s) = args[0].run(vars, info).data { if let Ok(s) = s.parse() { VDataEnum::Int(s).to() } else { @@ -808,7 +821,7 @@ impl BuiltinFunction { } BuiltinFunction::ParseFloat => { if args.len() == 1 { - if let VDataEnum::String(s) = args[0].run(vars, libs).data { + if let VDataEnum::String(s) = args[0].run(vars, info).data { if let Ok(s) = s.parse() { VDataEnum::Float(s).to() } else { @@ -823,15 +836,15 @@ impl BuiltinFunction { } BuiltinFunction::Run => { if args.len() >= 1 { - if let VDataEnum::Function(f) = args[0].run(vars, libs).data { + if let VDataEnum::Function(f) = args[0].run(vars, info).data { if f.inputs.len() != args.len() - 1 { unreachable!() } for (i, var) in f.inputs.iter().enumerate() { - let val = args[i + 1].run(vars, libs); + let val = args[i + 1].run(vars, info); *vars[*var].lock().unwrap() = val; } - f.run(vars, libs) + f.run(vars, info) } else { unreachable!() } @@ -841,7 +854,7 @@ impl BuiltinFunction { } BuiltinFunction::Thread => { if args.len() >= 1 { - if let VDataEnum::Function(f) = args[0].run(vars, libs).data { + if let VDataEnum::Function(f) = args[0].run(vars, info).data { if f.inputs.len() != args.len() - 1 { unreachable!() } @@ -849,12 +862,12 @@ impl BuiltinFunction { 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, libs); + let val = args[i + 1].run(vars, info); run_input_types.push(val.out_single()); thread_vars[*var] = Arc::new(Mutex::new(val)); } let out_type = f.out(&run_input_types); - let libs = libs.clone(); + let libs = info.clone(); VDataEnum::Thread( VDataThreadEnum::Running(std::thread::spawn(move || { f.run(&thread_vars, &libs) @@ -872,7 +885,7 @@ impl BuiltinFunction { } BuiltinFunction::Await => { if args.len() == 1 { - if let VDataEnum::Thread(t, _) = args[0].run(vars, libs).data { + if let VDataEnum::Thread(t, _) = args[0].run(vars, info).data { t.get() } else { unreachable!() @@ -883,7 +896,7 @@ impl BuiltinFunction { } BuiltinFunction::Sleep => { if args.len() == 1 { - match args[0].run(vars, libs).data { + match args[0].run(vars, info).data { VDataEnum::Int(v) => std::thread::sleep(Duration::from_secs(v as _)), VDataEnum::Float(v) => std::thread::sleep(Duration::from_secs_f64(v)), _ => unreachable!(), @@ -895,7 +908,7 @@ impl BuiltinFunction { } Self::Exit => { if let Some(s) = args.first() { - if let VDataEnum::Int(v) = s.run(vars, libs).data { + if let VDataEnum::Int(v) = s.run(vars, info).data { std::process::exit(v as _); } else { std::process::exit(1); @@ -906,7 +919,7 @@ impl BuiltinFunction { } Self::FsList => { if args.len() > 0 { - if let VDataEnum::String(path) = args[0].run(vars, libs).data { + if let VDataEnum::String(path) = args[0].run(vars, info).data { if args.len() > 1 { eprintln!("NOT YET IMPLEMENTED (TODO!): fs_list advanced filters") } @@ -944,7 +957,7 @@ impl BuiltinFunction { } Self::FsRead => { if args.len() > 0 { - if let VDataEnum::String(path) = args[0].run(vars, libs).data { + if let VDataEnum::String(path) = args[0].run(vars, info).data { match std::fs::read(path) { Ok(data) => VDataEnum::List( VSingleType::Int.into(), @@ -969,7 +982,7 @@ impl BuiltinFunction { Self::FsWrite => { if args.len() > 1 { if let (VDataEnum::String(path), VDataEnum::List(_, data)) = - (args[0].run(vars, libs).data, args[1].run(vars, libs).data) + (args[0].run(vars, info).data, args[1].run(vars, info).data) { if let Some(bytes) = vdata_to_bytes(&data) { let file_path: PathBuf = path.into(); @@ -998,7 +1011,7 @@ impl BuiltinFunction { } Self::BytesToString => { if args.len() == 1 { - if let VDataEnum::List(_, byte_data) = args[0].run(vars, libs).data { + if let VDataEnum::List(_, byte_data) = args[0].run(vars, info).data { if let Some(bytes) = vdata_to_bytes(&byte_data) { match String::from_utf8(bytes) { Ok(v) => VDataEnum::String(v).to(), @@ -1033,7 +1046,7 @@ impl BuiltinFunction { } Self::StringToBytes => { if args.len() == 1 { - if let VDataEnum::String(s) = args[0].run(vars, libs).data { + if let VDataEnum::String(s) = args[0].run(vars, info).data { VDataEnum::List( VSingleType::Int.into(), s.bytes().map(|v| VDataEnum::Int(v as isize).to()).collect(), @@ -1048,10 +1061,10 @@ impl BuiltinFunction { } Self::RunCommand | Self::RunCommandGetBytes => { if args.len() > 0 { - if let VDataEnum::String(s) = args[0].run(vars, libs).data { + if let VDataEnum::String(s) = args[0].run(vars, info).data { let mut command = std::process::Command::new(s); if args.len() > 1 { - if let VDataEnum::List(_, args) = args[1].run(vars, libs).data { + if let VDataEnum::List(_, args) = args[1].run(vars, info).data { for arg in args { if let VDataEnum::String(v) = arg.data { command.arg(v); @@ -1113,18 +1126,18 @@ impl BuiltinFunction { } } Self::Not => { - if let VDataEnum::Bool(v) = args[0].run(vars, libs).data { + if let VDataEnum::Bool(v) = args[0].run(vars, info).data { VDataEnum::Bool(!v).to() } else { unreachable!() } } Self::And => { - if let VDataEnum::Bool(a) = args[0].run(vars, libs).data { + if let VDataEnum::Bool(a) = args[0].run(vars, info).data { if a == false { VDataEnum::Bool(false).to() } else { - if let VDataEnum::Bool(b) = args[1].run(vars, libs).data { + if let VDataEnum::Bool(b) = args[1].run(vars, info).data { VDataEnum::Bool(b).to() } else { unreachable!() @@ -1135,11 +1148,11 @@ impl BuiltinFunction { } } Self::Or => { - if let VDataEnum::Bool(a) = args[0].run(vars, libs).data { + if let VDataEnum::Bool(a) = args[0].run(vars, info).data { if a == true { VDataEnum::Bool(true).to() } else { - if let VDataEnum::Bool(b) = args[1].run(vars, libs).data { + if let VDataEnum::Bool(b) = args[1].run(vars, info).data { VDataEnum::Bool(b).to() } else { unreachable!() @@ -1151,7 +1164,7 @@ impl BuiltinFunction { } Self::Add => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::String(mut a), VDataEnum::String(b)) => { a.push_str(b.as_str()); VDataEnum::String(a).to() @@ -1172,7 +1185,7 @@ impl BuiltinFunction { } Self::Sub => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a - b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 - b).to() @@ -1189,7 +1202,7 @@ impl BuiltinFunction { } Self::Mul => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a * b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 * b).to() @@ -1206,7 +1219,7 @@ impl BuiltinFunction { } Self::Div => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a / b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 / b).to() @@ -1223,7 +1236,7 @@ impl BuiltinFunction { } Self::Mod => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a % b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float(a as f64 % b).to() @@ -1240,7 +1253,7 @@ impl BuiltinFunction { } Self::Pow => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(if b == 0 { 1 } else if b > 0 { @@ -1266,14 +1279,14 @@ impl BuiltinFunction { } Self::Eq => { if args.len() == 2 { - VDataEnum::Bool(args[0].run(vars, libs) == args[1].run(vars, libs)).to() + VDataEnum::Bool(args[0].run(vars, info) == args[1].run(vars, info)).to() } else { unreachable!("eq: not 2 args") } } Self::Gt => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a > b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool(a as f64 > b).to() @@ -1290,7 +1303,7 @@ impl BuiltinFunction { } Self::Lt => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a < b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool((a as f64) < b).to() @@ -1307,7 +1320,7 @@ impl BuiltinFunction { } Self::Gtoe => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a >= b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool(a as f64 >= b).to() @@ -1324,7 +1337,7 @@ impl BuiltinFunction { } Self::Ltoe => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a <= b).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Bool(a as f64 <= b).to() @@ -1341,7 +1354,7 @@ impl BuiltinFunction { } Self::Min => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.min(b)).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float((a as f64).min(b)).to() @@ -1360,7 +1373,7 @@ impl BuiltinFunction { } Self::Max => { if args.len() == 2 { - match (args[0].run(vars, libs).data, args[1].run(vars, libs).data) { + match (args[0].run(vars, info).data, args[1].run(vars, info).data) { (VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.max(b)).to(), (VDataEnum::Int(a), VDataEnum::Float(b)) => { VDataEnum::Float((a as f64).max(b)).to() @@ -1379,9 +1392,9 @@ impl BuiltinFunction { } Self::Push => { if args.len() == 2 { - if let VDataEnum::Reference(v) = args[0].run(vars, libs).data { + if let VDataEnum::Reference(v) = args[0].run(vars, info).data { if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { - v.push(args[1].run(vars, libs)); + v.push(args[1].run(vars, info)); } VDataEnum::Tuple(vec![]).to() } else { @@ -1394,10 +1407,10 @@ impl BuiltinFunction { Self::Insert => { if args.len() == 3 { if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = - (args[0].run(vars, libs).data, args[2].run(vars, libs).data) + (args[0].run(vars, info).data, args[2].run(vars, info).data) { if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { - v.insert(i as _, args[1].run(vars, libs)); + v.insert(i as _, args[1].run(vars, info)); } VDataEnum::Tuple(vec![]).to() } else { @@ -1409,7 +1422,7 @@ impl BuiltinFunction { } Self::Pop => { if args.len() == 1 { - if let VDataEnum::Reference(v) = args[0].run(vars, libs).data { + if let VDataEnum::Reference(v) = args[0].run(vars, info).data { if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { if let Some(v) = v.pop() { VDataEnum::Tuple(vec![v]) @@ -1430,7 +1443,7 @@ impl BuiltinFunction { Self::Remove => { if args.len() == 2 { if let (VDataEnum::Reference(v), VDataEnum::Int(i)) = - (args[0].run(vars, libs).data, args[1].run(vars, libs).data) + (args[0].run(vars, info).data, args[1].run(vars, info).data) { if let VDataEnum::List(_, v) = &mut v.lock().unwrap().data { if v.len() > i as _ && i >= 0 { @@ -1452,7 +1465,7 @@ impl BuiltinFunction { Self::Get => { if args.len() == 2 { if let (container, VDataEnum::Int(i)) = - (args[0].run(vars, libs).data, args[1].run(vars, libs).data) + (args[0].run(vars, info).data, args[1].run(vars, info).data) { if i >= 0 { match match container { @@ -1484,7 +1497,7 @@ impl BuiltinFunction { } Self::Len => { if args.len() == 1 { - VDataEnum::Int(match args[0].run(vars, libs).data { + VDataEnum::Int(match args[0].run(vars, info).data { VDataEnum::String(v) => v.len(), VDataEnum::Tuple(v) => v.len(), VDataEnum::List(_, v) => v.len(), @@ -1497,8 +1510,8 @@ impl BuiltinFunction { } Self::Contains => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars, libs).data { - if let VDataEnum::String(a2) = args[1].run(vars, libs).data { + if let VDataEnum::String(a1) = args[0].run(vars, info).data { + if let VDataEnum::String(a2) = args[1].run(vars, info).data { VDataEnum::Bool(a1.contains(a2.as_str())).to() } else { unreachable!() @@ -1512,8 +1525,8 @@ impl BuiltinFunction { } Self::StartsWith => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars, libs).data { - if let VDataEnum::String(a2) = args[1].run(vars, libs).data { + if let VDataEnum::String(a1) = args[0].run(vars, info).data { + if let VDataEnum::String(a2) = args[1].run(vars, info).data { VDataEnum::Bool(a1.starts_with(a2.as_str())).to() } else { unreachable!() @@ -1527,8 +1540,8 @@ impl BuiltinFunction { } Self::EndsWith => { if args.len() == 2 { - if let VDataEnum::String(a1) = args[0].run(vars, libs).data { - if let VDataEnum::String(a2) = args[1].run(vars, libs).data { + if let VDataEnum::String(a1) = args[0].run(vars, info).data { + if let VDataEnum::String(a2) = args[1].run(vars, info).data { VDataEnum::Bool(a1.ends_with(a2.as_str())).to() } else { unreachable!() @@ -1542,8 +1555,8 @@ impl BuiltinFunction { } Self::IndexOf => { if args.len() == 2 { - let find_in = args[0].run(vars, libs); - let pat = args[1].run(vars, libs); + let find_in = args[0].run(vars, info); + let pat = args[1].run(vars, info); fn find(find_in: &String, pat: &String) -> VData { if let Some(found_byte_index) = find_in.find(pat) { if let Some(char_index) = find_in.char_indices().enumerate().find_map( @@ -1597,7 +1610,7 @@ impl BuiltinFunction { } Self::Trim => { if args.len() == 1 { - if let VDataEnum::String(a) = args[0].run(vars, libs).data { + if let VDataEnum::String(a) = args[0].run(vars, info).data { VDataEnum::String(a.trim().to_string()).to() } else { unreachable!() @@ -1608,17 +1621,17 @@ impl BuiltinFunction { } Self::Substring => { if args.len() >= 2 { - if let VDataEnum::String(a) = args[0].run(vars, libs).data { + if let VDataEnum::String(a) = args[0].run(vars, info).data { if args.len() > 3 { unreachable!() } - let left = if let VDataEnum::Int(left) = args[1].run(vars, libs).data { + let left = if let VDataEnum::Int(left) = args[1].run(vars, info).data { left } else { unreachable!() }; let len = if args.len() == 3 { - if let VDataEnum::Int(len) = args[2].run(vars, libs).data { + if let VDataEnum::Int(len) = args[2].run(vars, info).data { Some(len) } else { unreachable!() @@ -1656,7 +1669,7 @@ impl BuiltinFunction { Self::Regex => { if args.len() == 2 { if let (VDataEnum::String(a), VDataEnum::String(regex)) = - (args[0].run(vars, libs).data, args[1].run(vars, libs).data) + (args[0].run(vars, info).data, args[1].run(vars, info).data) { match regex::Regex::new(regex.as_str()) { Ok(regex) => VDataEnum::List( diff --git a/mers/src/script/code_parsed.rs b/mers/src/script/code_parsed.rs new file mode 100644 index 0000000..18bff7b --- /dev/null +++ b/mers/src/script/code_parsed.rs @@ -0,0 +1,235 @@ +use std::fmt::{self, Display, Formatter, Pointer}; + +use super::{global_info::GSInfo, val_data::VData, val_type::VType}; + +pub enum SStatementEnum { + Value(VData), + Tuple(Vec), + List(Vec), + Variable(String, bool), + FunctionCall(String, Vec), + FunctionDefinition(Option, SFunction), + Block(SBlock), + If(SStatement, SStatement, Option), + Loop(SStatement), + For(String, SStatement, SStatement), + Switch(String, Vec<(VType, SStatement)>, bool), + Match(String, Vec<(SStatement, SStatement)>), + IndexFixed(SStatement, usize), + EnumVariant(String, SStatement), +} +impl SStatementEnum { + pub fn to(self) -> SStatement { + SStatement { + output_to: None, + statement: Box::new(self), + force_output_type: None, + } + } +} + +pub struct SStatement { + pub output_to: Option<(String, usize)>, + pub statement: Box, + pub force_output_type: Option, +} + +impl SStatement { + pub fn new(statement: SStatementEnum) -> Self { + Self { + output_to: None, + statement: Box::new(statement), + force_output_type: None, + } + } + pub fn output_to(mut self, var: String, derefs: usize) -> Self { + self.output_to = Some((var, derefs)); + self + } + // forces the statement's output to fit in a certain type. + pub fn force_output_type(mut self, force_output_type: Option) -> Self { + self.force_output_type = force_output_type; + self + } +} + +/// A block of code is a collection of statements. +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. +pub struct SFunction { + pub inputs: Vec<(String, VType)>, + pub block: SBlock, +} +impl SFunction { + pub fn new(inputs: Vec<(String, VType)>, block: SBlock) -> Self { + Self { inputs, block } + } +} + +// + +impl SStatementEnum { + pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GSInfo>) -> fmt::Result { + match self { + Self::Value(v) => v.fmtgs(f, info), + Self::Tuple(v) => { + write!(f, "[")?; + for (i, v) in v.iter().enumerate() { + if i > 0 { + write!(f, " ")?; + } + v.fmtgs(f, info)?; + } + write!(f, "]") + } + Self::List(v) => { + write!(f, "[")?; + for (i, v) in v.iter().enumerate() { + v.fmtgs(f, info)?; + write!(f, " ")?; + } + write!(f, "...]") + } + Self::Variable(var, reference) => { + if *reference { + write!(f, "&{var}") + } else { + write!(f, "{var}") + } + } + Self::FunctionCall(func, args) => { + write!(f, "{func}(")?; + for arg in args { + arg.fmtgs(f, info)?; + } + write!(f, ")") + } + Self::FunctionDefinition(name, func) => { + if let Some(name) = name { + write!(f, "{name}")?; + } + func.fmtgs(f, info) + } + Self::Block(b) => b.fmtgs(f, info), + Self::If(condition, yes, no) => { + write!(f, "if ")?; + condition.fmtgs(f, info)?; + write!(f, " ")?; + yes.fmtgs(f, info)?; + if let Some(no) = no { + write!(f, " else ")?; + no.fmtgs(f, info)?; + } + Ok(()) + } + Self::Loop(b) => { + write!(f, "loop ")?; + b.fmtgs(f, info) + } + Self::For(var, i, b) => { + write!(f, "for {} ", var)?; + i.fmtgs(f, info)?; + write!(f, " ")?; + b.fmtgs(f, info) + } + Self::Switch(var, arms, force) => { + if *force { + writeln!(f, "switch! {var} {{")?; + } else { + writeln!(f, "switch {var} {{")?; + } + for (t, action) in arms { + t.fmtgs(f, info)?; + write!(f, " ")?; + action.fmtgs(f, info)?; + writeln!(f)?; + } + write!(f, "}}") + } + Self::Match(var, arms) => { + write!(f, "match {var} {{")?; + for (condition, action) in arms { + condition.fmtgs(f, info)?; + write!(f, " ")?; + action.fmtgs(f, info)?; + writeln!(f)?; + } + write!(f, "}}") + } + Self::IndexFixed(statement, index) => { + statement.fmtgs(f, info)?; + write!(f, ".{index}") + } + Self::EnumVariant(variant, inner) => { + write!(f, "{variant}: ")?; + inner.fmtgs(f, info) + } + } + } +} +impl Display for SStatementEnum { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.fmtgs(f, None) + } +} + +impl SStatement { + pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GSInfo>) -> fmt::Result { + if let Some((opt, derefs)) = &self.output_to { + if let Some(forced_type) = &self.force_output_type { + write!(f, "{}{}::", "*".repeat(*derefs), opt)?; + forced_type.fmtgs(f, info)?; + write!(f, " = ")?; + } else { + write!(f, "{}{} = ", "*".repeat(*derefs), opt)?; + } + } + self.statement.fmtgs(f, info) + } +} +impl Display for SStatement { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.fmtgs(f, None) + } +} + +impl SFunction { + pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GSInfo>) -> fmt::Result { + write!(f, "(")?; + for (i, (name, t)) in self.inputs.iter().enumerate() { + if i > 0 { + write!(f, " {name}")?; + } else { + write!(f, "{name} ")?; + } + t.fmtgs(f, info)?; + } + write!(f, ") ")?; + self.block.fmtgs(f, info) + } +} + +impl SBlock { + pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GSInfo>) -> fmt::Result { + match self.statements.len() { + 0 => write!(f, "{{}}"), + 1 => self.statements[0].fmtgs(f, info), + _ => { + writeln!(f, "{{")?; + for statement in self.statements.iter() { + statement.fmtgs(f, info)?; + writeln!(f)?; + } + write!(f, "}}") + } + } + } +} diff --git a/mers/src/script/code_runnable.rs b/mers/src/script/code_runnable.rs new file mode 100644 index 0000000..2c88d56 --- /dev/null +++ b/mers/src/script/code_runnable.rs @@ -0,0 +1,391 @@ +use std::sync::{Arc, Mutex}; + +use super::{ + builtins::BuiltinFunction, + global_info::GSInfo, + to_runnable::ToRunnableError, + val_data::{VData, VDataEnum}, + val_type::{VSingleType, VType}, +}; + +type Am = Arc>; +fn am(i: T) -> Am { + Arc::new(Mutex::new(i)) +} + +#[derive(Clone, Debug)] +pub struct RBlock { + pub statements: Vec, +} +impl RBlock { + pub fn run(&self, vars: &Vec>, info: &GSInfo) -> VData { + let mut last = None; + for statement in &self.statements { + last = Some(statement.run(vars, info)); + } + 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>, info: &GSInfo) -> VData { + self.block.run(vars, info) + } + 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 { + pub output_to: Option<(usize, usize)>, + statement: Box, + pub force_output_type: Option, +} +impl RStatement { + pub fn run(&self, vars: &Vec>, info: &GSInfo) -> VData { + let out = self.statement.run(vars, info); + if let Some((v, derefs)) = self.output_to { + let mut val = vars[v].clone(); + for _ in 0..derefs { + let v = if let VDataEnum::Reference(v) = &val.lock().unwrap().data { + v.clone() + } else { + unreachable!("dereferencing something that isn't a reference in assignment") + }; + val = v; + } + *val.lock().unwrap() = out; + VDataEnum::Tuple(vec![]).to() + } else { + out + } + } + pub fn out(&self) -> VType { + // `a = b` evaluates to [] + if self.output_to.is_some() { + return VType { + types: vec![VSingleType::Tuple(vec![])], + }; + } + if let Some(t) = &self.force_output_type { + return t.clone(); + } + self.statement.out() + } +} + +#[derive(Clone, Debug)] +pub enum RStatementEnum { + Value(VData), + Tuple(Vec), + List(Vec), + Variable(usize, VType, bool), + FunctionCall(Arc, Vec), + BuiltinFunction(BuiltinFunction, Vec), + LibFunction(usize, usize, Vec, VType), + Block(RBlock), + If(RStatement, RStatement, Option), + Loop(RStatement), + For(usize, RStatement, RStatement), + Switch(RStatement, Vec<(VType, RStatement)>), + Match(usize, Vec<(RStatement, RStatement)>), + IndexFixed(RStatement, usize), + EnumVariant(usize, RStatement), +} +impl RStatementEnum { + pub fn run(&self, vars: &Vec>, info: &GSInfo) -> VData { + match self { + Self::Value(v) => v.clone(), + Self::Tuple(v) => { + let mut w = vec![]; + for v in v { + w.push(v.run(vars, info)); + } + 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, info); + 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, info); + } + func.run(vars, info) + } + Self::BuiltinFunction(v, args) => v.run(args, vars, info), + Self::LibFunction(libid, fnid, args, _) => info.libs[*libid] + .run_fn(*fnid, &args.iter().map(|arg| arg.run(vars, info)).collect()), + Self::Block(b) => b.run(vars, info), + Self::If(c, t, e) => { + if let VDataEnum::Bool(v) = c.run(vars, info).data { + if v { + t.run(vars, info) + } else { + if let Some(e) = e { + e.run(vars, info) + } else { + VDataEnum::Tuple(vec![]).to() + } + } + } else { + unreachable!() + } + } + Self::Loop(c) => loop { + // While loops will break if the value matches. + if let Some(break_val) = c.run(vars, info).data.matches() { + break break_val; + } + }, + Self::For(v, c, b) => { + // matching values also break with value from a for loop. + let c = c.run(vars, info); + let mut vars = vars.clone(); + let in_loop = |vars: &mut Vec>>, c| { + vars[*v] = Arc::new(Mutex::new(c)); + b.run(&vars, info) + }; + + let mut oval = VDataEnum::Tuple(vec![]).to(); + match c.data { + VDataEnum::Int(v) => { + for i in 0..v { + if let Some(v) = + in_loop(&mut vars, VDataEnum::Int(i).to()).data.matches() + { + oval = v; + break; + } + } + } + VDataEnum::String(v) => { + for ch in v.chars() { + if let Some(v) = + in_loop(&mut vars, VDataEnum::String(ch.to_string()).to()) + .data + .matches() + { + oval = v; + break; + } + } + } + VDataEnum::Tuple(v) | VDataEnum::List(_, v) => { + for v in v { + if let Some(v) = in_loop(&mut vars, v).data.matches() { + oval = v; + break; + } + } + } + VDataEnum::Function(f) => loop { + if let Some(v) = f.run(&vars, info).data.matches() { + if let Some(v) = in_loop(&mut vars, v).data.matches() { + oval = v; + break; + } + } else { + break; + } + }, + _ => unreachable!(), + } + oval + } + Self::Switch(switch_on, cases) => { + let switch_on = switch_on.run(vars, info); + 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, info); + break; + } + } + out + } + Self::Match(match_on, cases) => 'm: { + for (case_condition, case_action) in cases { + // [t] => Some(t), t => Some(t), [] | false => None + if let Some(v) = case_condition.run(vars, info).data.matches() { + let og = { std::mem::replace(&mut *vars[*match_on].lock().unwrap(), v) }; + let res = case_action.run(vars, info); + *vars[*match_on].lock().unwrap() = og; + break 'm res; + } + } + VDataEnum::Tuple(vec![]).to() + } + Self::IndexFixed(st, i) => st.run(vars, info).get(*i).unwrap(), + Self::EnumVariant(e, v) => VDataEnum::EnumVariant(*e, Box::new(v.run(vars, info))).to(), + } + } + 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::LibFunction(.., out) => out.clone(), + Self::Block(b) => b.out(), + Self::If(_, a, b) => { + if let Some(b) = b { + a.out() | b.out() + } else { + a.out() | VSingleType::Tuple(vec![]).to() + } + } + Self::Loop(c) => c.out().matches().1, + Self::For(_, _, b) => VSingleType::Tuple(vec![]).to() | b.out().matches().1, + 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 = switch_on.to(); + '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(), + Self::EnumVariant(e, v) => VSingleType::EnumVariant(*e, v.out()).to(), + } + } + pub fn to(self) -> RStatement { + RStatement { + output_to: None, + statement: Box::new(self), + force_output_type: None, + } + } +} + +pub struct RScript { + main: RFunction, + vars: usize, + info: GSInfo, +} +impl RScript { + pub fn new(main: RFunction, vars: usize, info: GSInfo) -> Result { + if main.inputs.len() != 1 { + return Err(ToRunnableError::MainWrongInput); + } + Ok(Self { main, vars, info }) + } + 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, &self.info) + } +} diff --git a/mers/src/script/global_info.rs b/mers/src/script/global_info.rs new file mode 100644 index 0000000..090b2b3 --- /dev/null +++ b/mers/src/script/global_info.rs @@ -0,0 +1,15 @@ +use std::{collections::HashMap, sync::Arc}; + +use crate::libs; + +pub type GSInfo = Arc; +// pub type GSMInfo = Arc>; +pub struct GlobalScriptInfo { + pub libs: Vec, + pub enums: HashMap, +} +impl GlobalScriptInfo { + pub fn to_arc(self) -> GSInfo { + Arc::new(self) + } +} diff --git a/mers/src/script/mod.rs b/mers/src/script/mod.rs index 3ca51de..a0c5979 100755 --- a/mers/src/script/mod.rs +++ b/mers/src/script/mod.rs @@ -1,4 +1,7 @@ -pub mod block; pub mod builtins; +pub mod code_parsed; +pub mod code_runnable; +pub mod global_info; +pub mod to_runnable; pub mod val_data; pub mod val_type; diff --git a/mers/src/script/to_runnable.rs b/mers/src/script/to_runnable.rs new file mode 100644 index 0000000..f951415 --- /dev/null +++ b/mers/src/script/to_runnable.rs @@ -0,0 +1,629 @@ +use std::{ + collections::HashMap, + fmt::{Debug, Display}, + sync::Arc, +}; + +use crate::{ + libs, + script::{ + builtins, + global_info::GlobalScriptInfo, + val_data::VDataEnum, + val_type::{VSingleType, VType}, + }, +}; + +use super::{ + builtins::BuiltinFunction, + code_parsed::{SBlock, SFunction, SStatement, SStatementEnum}, + code_runnable::{RBlock, RFunction, RScript, RStatement, RStatementEnum}, +}; + +pub enum ToRunnableError { + MainWrongInput, + UseOfUndefinedVariable(String), + UseOfUndefinedFunction(String), + CannotDeclareVariableWithDereference(String), + CannotDereferenceTypeNTimes(VType, usize, VType), + FunctionWrongArgCount(String, usize, usize), + InvalidType { + expected: VType, + found: VType, + problematic: VType, + }, + CaseForceButTypeNotCovered(VType), + MatchConditionInvalidReturn(VType), + NotIndexableFixed(VType, usize), + WrongInputsForBuiltinFunction(BuiltinFunction, String, Vec), + WrongArgsForLibFunction(String, Vec), + ForLoopContainerHasNoInnerTypes, + StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(VType, VType, VType), +} +impl Debug for ToRunnableError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self}") + } +} +// TODO: +// - Don't use {} to format, use .fmtgs(f, info) instead! +// - Show location in code where the error was found +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::CannotDeclareVariableWithDereference(v) => write!(f, "Cannot declare a variable and dereference it (variable '{v}')."), + Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => write!(f, + "Cannot dereference type {og_type} {derefs_wanted} times (stopped at {last_valid_type})." + ), + 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::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}."), + Self::WrongInputsForBuiltinFunction(_builtin, builtin_name, args) => { + write!(f, "Wrong arguments for builtin function {}:", builtin_name)?; + for arg in args { + write!(f, " {arg}")?; + } + write!(f, ".") + } + Self::WrongArgsForLibFunction(name, args) => { + write!(f, "Wrong arguments for library function {}:", name)?; + for arg in args { + write!(f, " {arg}")?; + } + write!(f, ".") + } + Self::ForLoopContainerHasNoInnerTypes => { + write!(f, "For loop: container had no inner types, cannot iterate.") + } + Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => write!(f, + "the statement requires its output type to be {required}, but its real output type is {real}, which doesn not fit in the required type because of the problematic types {problematic}." + ), + } + } +} + +// Global, shared between all +pub struct GInfo { + vars: usize, + pub libs: Vec, + pub lib_fns: HashMap, + pub enum_variants: HashMap, +} +impl Default for GInfo { + fn default() -> Self { + Self { + vars: 0, + libs: vec![], + lib_fns: HashMap::new(), + enum_variants: Self::default_enum_variants(), + } + } +} +impl GInfo { + pub fn default_enum_variants() -> HashMap { + builtins::EVS + .iter() + .enumerate() + .map(|(i, v)| (v.to_string(), i)) + .collect() + } + pub fn new(libs: Vec, enum_variants: HashMap) -> Self { + let mut lib_fns = HashMap::new(); + for (libid, lib) in libs.iter().enumerate() { + for (fnid, (name, ..)) in lib.registered_fns.iter().enumerate() { + lib_fns.insert(name.to_string(), (libid, fnid)); + } + } + Self { + vars: 0, + libs, + lib_fns, + enum_variants, + } + } +} +// Local, used to keep local variables separated +#[derive(Clone)] +struct LInfo { + vars: HashMap, + fns: HashMap>, +} + +pub fn to_runnable(s: SFunction, mut ginfo: GInfo) -> Result { + if s.inputs.len() != 1 || s.inputs[0].0 != "args" { + return Err(ToRunnableError::MainWrongInput); + } + assert_eq!( + s.inputs[0].1, + VType { + types: vec![VSingleType::List(VType { + types: vec![VSingleType::String], + })], + } + ); + let func = function( + &s, + &mut ginfo, + LInfo { + vars: HashMap::new(), + fns: HashMap::new(), + }, + )?; + Ok(RScript::new( + func, + ginfo.vars, + GlobalScriptInfo { + libs: ginfo.libs, + enums: ginfo.enum_variants, + } + .to_arc(), + )?) +} + +// 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 stypes(t: &mut VType, ginfo: &mut GInfo) { + for t in &mut t.types { + stype(t, ginfo); + } +} +fn stype(t: &mut VSingleType, ginfo: &mut GInfo) { + match t { + VSingleType::Tuple(v) => { + for t in v { + stypes(t, ginfo); + } + } + VSingleType::EnumVariantS(e, v) => { + *t = VSingleType::EnumVariant( + { + if let Some(v) = ginfo.enum_variants.get(e) { + *v + } else { + let v = ginfo.enum_variants.len(); + ginfo.enum_variants.insert(e.clone(), v); + v + } + }, + { + stypes(v, ginfo); + v.clone() + }, + ) + } + _ => (), + } +} +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, { + let mut v = var.1.clone(); stypes(&mut v, ginfo); v }, *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: VType { types: out }, + }); + } + } + RStatementEnum::FunctionCall(func.clone(), rargs) + } else { + // TODO: type-checking for builtins + if let Some(builtin) = BuiltinFunction::get(v) { + let arg_types = rargs.iter().map(|v| v.out()).collect(); + if builtin.can_take(&arg_types) { + RStatementEnum::BuiltinFunction(builtin, rargs) + } else { + return Err(ToRunnableError::WrongInputsForBuiltinFunction(builtin, v.to_string(), arg_types)); + } + } else { + // LIBRARY FUNCTION? + if let Some((libid, fnid)) = ginfo.lib_fns.get(v) { + let (_name, fn_in, fn_out) = &ginfo.libs[*libid].registered_fns[*fnid]; + if fn_in.len() == rargs.len() && fn_in.iter().zip(rargs.iter()).all(|(fn_in, arg)| arg.out().fits_in(fn_in).is_empty()) { + RStatementEnum::LibFunction(*libid, *fnid, rargs, fn_out.clone()) + } else { + // TODO! better error here + return Err(if fn_in.len() == rargs.len() { + ToRunnableError::WrongArgsForLibFunction(v.to_string(), rargs.iter().map(|v| v.out()).collect()) + } else { + ToRunnableError::FunctionWrongArgCount(v.to_string(), fn_in.len(), rargs.len()) + }); + } + } 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: VType { types: out }, + }); + } + }, + statement(&t, ginfo, linfo)?, + match e { + Some(v) => Some(statement(&v, ginfo, linfo)?), + None => None, + }, + ), + SStatementEnum::Loop(c) => RStatementEnum::Loop( + statement(&c, ginfo, linfo)? + ), + SStatementEnum::For(v, c, b) => { + let mut linfo = linfo.clone(); + let container = statement(&c, ginfo, &mut linfo)?; + let inner = container.out().inner_types(); + if inner.types.is_empty() { + return Err(ToRunnableError::ForLoopContainerHasNoInnerTypes); + } + linfo + .vars + .insert(v.clone(), (ginfo.vars, inner)); + 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 { + let case0 = { let mut v = case.0.clone(); stypes(&mut v, ginfo); v }; + linfo.vars.get_mut(switch_on).unwrap().1 = case0.clone(); + ncases.push((case0, 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 { + let mut ct = case_type.clone(); + stypes(&mut ct, ginfo); + if val_type.fits_in(&ct).is_empty() { + break 'force; + } + } + types_not_covered_req_error = true; + types_not_covered = types_not_covered | { + let mut v = val_type; + fn make_readable(v: &mut VType, ginfo: &GInfo) { + for t in v.types.iter_mut() { + match t { + VSingleType::EnumVariant(i, v) => { + let mut v = v.clone(); + make_readable(&mut v, ginfo); + *t = VSingleType::EnumVariantS(ginfo.enum_variants.iter().find_map(|(st, us)| if *us == *i { Some(st.clone()) } else { None }).unwrap(), v); + }, + VSingleType::EnumVariantS(_, v) => make_readable(v, ginfo), + VSingleType::Tuple(v) => for t in v.iter_mut() { + make_readable(t, ginfo) + } + VSingleType::List(t) => make_readable(t, ginfo), + VSingleType::Reference(v) => { + let mut v = v.clone().to(); + make_readable(&mut v, ginfo); + assert_eq!(v.types.len(), 1); + *t = VSingleType::Reference(Box::new(v.types.remove(0))); + } + VSingleType::Bool | VSingleType::Int | VSingleType::Float | VSingleType::String | VSingleType::Function(..) | VSingleType::Thread(..) => (), + } + } + } + make_readable(&mut v, &ginfo); + v + }; + } + } + 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)); + } + } + SStatementEnum::EnumVariant(variant, s) => RStatementEnum::EnumVariant({ + if let Some(v) = ginfo.enum_variants.get(variant) { + *v + } else { + let v = ginfo.enum_variants.len(); + ginfo.enum_variants.insert(variant.clone(), v); + v + } + }, statement(s, ginfo, linfo)?), + } + .to(); + // if force_output_type is set, verify that the real output type actually fits in the forced one. + if let Some(force_opt) = &s.force_output_type { + let real_output_type = statement.out(); + let problematic_types = real_output_type.fits_in(force_opt); + if problematic_types.is_empty() { + statement.force_output_type = Some(force_opt.clone()); + } else { + return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types })); + } + } + if let Some((opt, derefs)) = &s.output_to { + if let Some((var_id, var_out)) = linfo.vars.get(opt) { + let out = statement.out(); + let mut var_derefd = var_out.clone(); + for _ in 0..*derefs { + var_derefd = if let Some(v) = var_derefd.dereference() { + v + } else { + return Err(ToRunnableError::CannotDereferenceTypeNTimes( + var_out.clone(), + *derefs, + var_derefd, + )); + } + } + let inv_types = out.fits_in(&var_derefd); + 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."); + if *derefs != 0 { + return Err(ToRunnableError::CannotDeclareVariableWithDereference( + opt.clone(), + )); + } + linfo.vars.insert(opt.clone(), (ginfo.vars, out)); + statement.output_to = Some((ginfo.vars, 0)); + ginfo.vars += 1; + } else { + // mutate existing variable + statement.output_to = Some((*var_id, *derefs)); + } + } else { + let mut out = statement.out(); + for _ in 0..*derefs { + out = if let Some(v) = out.dereference() { + v + } else { + return Err(ToRunnableError::CannotDereferenceTypeNTimes( + statement.out(), + *derefs, + out, + )); + } + } + linfo.vars.insert(opt.clone(), (ginfo.vars, out)); + statement.output_to = Some((ginfo.vars, *derefs)); + ginfo.vars += 1; + } + } + Ok(statement) +} diff --git a/mers/src/script/val_data.rs b/mers/src/script/val_data.rs index 9ebdcb2..6e19b30 100755 --- a/mers/src/script/val_data.rs +++ b/mers/src/script/val_data.rs @@ -1,16 +1,16 @@ use std::{ - fmt::Debug, + fmt::{self, Debug, Display, Formatter}, sync::{Arc, Mutex}, }; use super::{ - block::RFunction, + code_runnable::RFunction, + global_info::GSInfo, val_type::{VSingleType, VType}, }; #[derive(Clone, Debug, PartialEq)] pub struct VData { - // parents: Vec<()>, pub data: VDataEnum, } @@ -213,7 +213,7 @@ pub mod thread { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &*self.lock() { VDataThreadEnum::Running(_) => write!(f, "(thread running)"), - VDataThreadEnum::Finished(v) => write!(f, "(thread finished: {v})"), + VDataThreadEnum::Finished(v) => write!(f, "(thread finished)"), } } } @@ -227,3 +227,85 @@ pub mod thread { } } } + +// + +pub struct VDataWInfo(VData, GSInfo); +impl Display for VDataWInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.0.fmtgs(f, Some(&self.1)) + } +} +impl VData { + pub fn gsi(self, info: GSInfo) -> VDataWInfo { + VDataWInfo(self, info) + } +} + +impl VDataEnum { + pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GSInfo>) -> fmt::Result { + match self { + Self::Bool(true) => write!(f, "true"), + Self::Bool(false) => write!(f, "false"), + Self::Int(v) => write!(f, "{v}"), + Self::Float(v) => write!(f, "{v}"), + Self::String(v) => write!(f, "\"{v}\""), + Self::Tuple(v) => { + write!(f, "[")?; + for (i, v) in v.iter().enumerate() { + if i > 0 { + write!(f, " ")?; + } + v.fmtgs(f, info)?; + } + write!(f, "]") + } + Self::List(_t, v) => { + write!(f, "[")?; + for (i, v) in v.iter().enumerate() { + v.fmtgs(f, info)?; + write!(f, " ")?; + } + write!(f, "...]") + } + Self::Function(func) => { + VSingleType::Function(func.input_output_map.clone()).fmtgs(f, info) + } + Self::Thread(..) => write!(f, "[TODO] THREAD"), + Self::Reference(inner) => { + write!(f, "&")?; + inner.lock().unwrap().fmtgs(f, info) + } + Self::EnumVariant(variant, inner) => { + if let Some(name) = if let Some(info) = info { + info.enums + .iter() + .find_map(|(name, id)| if id == variant { Some(name) } else { None }) + } else { + None + } { + write!(f, "{name}: ")?; + } else { + write!(f, "{variant}: ")?; + } + inner.fmtgs(f, info) + } + } + } +} +impl Display for VDataEnum { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.fmtgs(f, None) + } +} + +impl VData { + pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GSInfo>) -> fmt::Result { + self.data.fmtgs(f, info) + } +} +impl Display for VData { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.fmtgs(f, None) + } +} diff --git a/mers/src/script/val_type.rs b/mers/src/script/val_type.rs index a34b7eb..0b15d3b 100755 --- a/mers/src/script/val_type.rs +++ b/mers/src/script/val_type.rs @@ -1,4 +1,10 @@ -use std::{collections::HashMap, fmt::Debug, ops::BitOr}; +use std::{ + collections::HashMap, + fmt::{self, Debug, Display, Formatter}, + ops::BitOr, +}; + +use super::global_info::GSInfo; #[derive(Clone, Debug, PartialEq, Eq)] pub struct VType { @@ -18,6 +24,8 @@ pub enum VSingleType { Reference(Box), EnumVariant(usize, VType), EnumVariantS(String, VType), + // CustomType(usize), + // CustomTypeS(String), } impl VSingleType { @@ -299,3 +307,107 @@ impl Into for VSingleType { VType { types: vec![self] } } } + +// + +pub struct VTypeWInfo(VType, GSInfo); +impl Display for VTypeWInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.0.fmtgs(f, Some(&self.1)) + } +} +impl VType { + pub fn gsi(self, info: GSInfo) -> VTypeWInfo { + VTypeWInfo(self, info) + } +} + +impl VSingleType { + pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GSInfo>) -> 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(v) => { + write!(f, "[")?; + for (i, v) in v.iter().enumerate() { + if i > 0 { + write!(f, " ")?; + } + v.fmtgs(f, info)?; + } + write!(f, "]") + } + Self::List(v) => { + write!(f, "[")?; + v.fmtgs(f, info)?; + write!(f, " ...]") + } + Self::Function(func) => { + write!(f, "fn(")?; + for (inputs, output) in func { + write!(f, "(")?; + for i in inputs { + i.fmtgs(f, info)?; + write!(f, " "); + } + output.fmtgs(f, info)?; + write!(f, ")")?; + } + write!(f, ")") + } + Self::Thread(out) => { + write!(f, "thread(")?; + out.fmtgs(f, info)?; + write!(f, ")") + } + Self::Reference(inner) => { + write!(f, "&")?; + inner.fmtgs(f, info) + } + Self::EnumVariant(variant, inner) => { + if let Some(name) = if let Some(info) = info { + info.enums + .iter() + .find_map(|(name, id)| if id == variant { Some(name) } else { None }) + } else { + None + } { + write!(f, "{name}(")?; + } else { + write!(f, "{variant}(")?; + } + inner.fmtgs(f, info)?; + write!(f, ")") + } + Self::EnumVariantS(name, inner) => { + write!(f, "{name}(")?; + inner.fmtgs(f, info)?; + write!(f, ")") + } + } + } +} +impl Display for VSingleType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.fmtgs(f, None) + } +} + +impl VType { + pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GSInfo>) -> fmt::Result { + for (i, t) in self.types.iter().enumerate() { + if i > 0 { + write!(f, "/")?; + } + t.fmtgs(f, info)?; + } + Ok(()) + } +} +impl Display for VType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.fmtgs(f, None) + } +} diff --git a/mers/src/script/value.rs b/mers/src/script/value.rs deleted file mode 100755 index 92da94a..0000000 --- a/mers/src/script/value.rs +++ /dev/null @@ -1 +0,0 @@ -// moved to val_data and val_type diff --git a/mers/src/tutor/base_functions.rs b/mers/src/tutor/base_functions.rs index 48b92a1..0c0bcc3 100644 --- a/mers/src/tutor/base_functions.rs +++ b/mers/src/tutor/base_functions.rs @@ -25,6 +25,11 @@ fn say_hello_world() { println(\"Hello, world!\") } +// Since the Subject.Verb(Object) syntax is more natural to many people, a.function(b c d) is an alternative way of writing function(a b c d): +my_var = 15 +format(\"my variable had the value {0}!\" my_var) // normal +\"my variable had the value {0}!\".format(my_var) // alternative (does the same thing) + // to return to the menu, add two arguments to the mul() function to make it return 32*5 mul() ", diff --git a/mers/src/tutor/base_types.rs b/mers/src/tutor/base_types.rs index ca932fb..85b6f2e 100644 --- a/mers/src/tutor/base_types.rs +++ b/mers/src/tutor/base_types.rs @@ -106,6 +106,9 @@ switch! x {} // And this doesn't even rely on any fancy technology, it's all just based on simple and intuitive rules: // x is the result of an if-else statement, meaning x's type is just the return types from the two branches combined: int and string -> int/string. // if we remove the else, x's type would be int/[]: either an int or nothing. +// Instead of using switch! to get a compiler error, you can also look at the types at runtime using the builtin debug() function: +// x.debug() // (or debug(x)) | this outputs 'int/string :: int :: 10' | debug()'s format is: +// the statement's type (this is constant and determined at compile-time) :: the value's type (this can change, but it's always one of the types in the constant type) :: string-representation of the value // (don't forget to comment out the third switch statement, too. you can't return to the menu otherwise) // By combining multiple-types with tuples, we can express complicated data structures without needing structs or enums: diff --git a/mers/src/tutor/mod.rs b/mers/src/tutor/mod.rs index 143b56d..f609dc0 100644 --- a/mers/src/tutor/mod.rs +++ b/mers/src/tutor/mod.rs @@ -2,7 +2,7 @@ use std::{path::PathBuf, thread::JoinHandle, time::Instant}; use crate::{ parse::{self, parse::ScriptError}, - script::{block::RScript, val_data::VDataEnum}, + script::{code_runnable::RScript, val_data::VDataEnum}, }; mod base_comments; diff --git a/musicdb_remote.mers b/musicdb_remote.mers index 7472cac..86edb1b 100755 --- a/musicdb_remote.mers +++ b/musicdb_remote.mers @@ -117,7 +117,7 @@ fn stop() { } } fn queue_song(song_id int) { - make_api_request("q+{0}".format(song_id)) + make_api_request("q+{0}".format(song_id.to_string())) } // fetch the database contents (a list of songs) diff --git a/thread.mers b/thread.mers index 36b3ad7..702e130 100755 --- a/thread.mers +++ b/thread.mers @@ -16,7 +16,7 @@ calculator = (max int) { if fake_delay sleep(1) progress = i.mul(100).div(max) } - "the sum of all numbers from 0 to {0} is {1}!".format(max sum) + "the sum of all numbers from 0 to {0} is {1}!".format(max.to_string() sum.to_string()) } // start the thread. if fake_delay is true, calculate 1+2+3+4+5+6+7+8+9+10. if fake_delay is false, count up to some ridiculously large number. @@ -25,7 +25,7 @@ slow_calculation_thread = calculator.thread(if fake_delay 10 else 20000000) // every second, print the progress. once it reaches 100%, stop while { sleep(1) - println("{0}%".format(progress)) + println("{0}%".format(progress.to_string())) progress.eq(100) // break from the loop }