mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
- 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
This commit is contained in:
parent
99f8c504a4
commit
89cc7971ee
18
README.md
18
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")
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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<RScript, ScriptError> {
|
||||
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<SFunction, ParseError> {
|
||||
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<RScript, ToRunnableError> {
|
||||
pub fn parse_step_compile(main_func: SFunction, ginfo: GInfo) -> Result<RScript, ToRunnableError> {
|
||||
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<super::file::FilePosition>)>,
|
||||
)>,
|
||||
info: Option<GSInfo>,
|
||||
}
|
||||
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<ParseError>),
|
||||
}
|
||||
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,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<RStatement>,
|
||||
vars: &Vec<Arc<Mutex<VData>>>,
|
||||
libs: &Arc<Vec<libs::Lib>>,
|
||||
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(
|
||||
|
235
mers/src/script/code_parsed.rs
Normal file
235
mers/src/script/code_parsed.rs
Normal file
@ -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<SStatement>),
|
||||
List(Vec<SStatement>),
|
||||
Variable(String, bool),
|
||||
FunctionCall(String, Vec<SStatement>),
|
||||
FunctionDefinition(Option<String>, SFunction),
|
||||
Block(SBlock),
|
||||
If(SStatement, SStatement, Option<SStatement>),
|
||||
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<SStatementEnum>,
|
||||
pub force_output_type: Option<VType>,
|
||||
}
|
||||
|
||||
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<VType>) -> Self {
|
||||
self.force_output_type = force_output_type;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A block of code is a collection of statements.
|
||||
pub struct SBlock {
|
||||
pub statements: Vec<SStatement>,
|
||||
}
|
||||
impl SBlock {
|
||||
pub fn new(statements: Vec<SStatement>) -> Self {
|
||||
Self { statements }
|
||||
}
|
||||
}
|
||||
|
||||
// A function is a block of code that starts with some local variables as inputs and returns some value as its output. The last statement in the block will be the output.
|
||||
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, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
391
mers/src/script/code_runnable.rs
Normal file
391
mers/src/script/code_runnable.rs
Normal file
@ -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<T> = Arc<Mutex<T>>;
|
||||
fn am<T>(i: T) -> Am<T> {
|
||||
Arc::new(Mutex::new(i))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RBlock {
|
||||
pub statements: Vec<RStatement>,
|
||||
}
|
||||
impl RBlock {
|
||||
pub fn run(&self, vars: &Vec<Am<VData>>, 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<usize>,
|
||||
pub input_types: Vec<VType>,
|
||||
pub input_output_map: Vec<(Vec<VSingleType>, VType)>,
|
||||
pub block: RBlock,
|
||||
}
|
||||
impl RFunction {
|
||||
pub fn run(&self, vars: &Vec<Am<VData>>, info: &GSInfo) -> VData {
|
||||
self.block.run(vars, info)
|
||||
}
|
||||
pub fn out(&self, input_types: &Vec<VSingleType>) -> VType {
|
||||
self.input_output_map
|
||||
.iter()
|
||||
.find_map(|v| {
|
||||
if v.0 == *input_types {
|
||||
Some(v.1.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.expect("invalid args for function! possible issue with type-checker if this can be reached! feel free to report a bug.")
|
||||
}
|
||||
pub fn out_vt(&self, input_types: &Vec<VType>) -> 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<VType> {
|
||||
&self.input_types
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RStatement {
|
||||
pub output_to: Option<(usize, usize)>,
|
||||
statement: Box<RStatementEnum>,
|
||||
pub force_output_type: Option<VType>,
|
||||
}
|
||||
impl RStatement {
|
||||
pub fn run(&self, vars: &Vec<Am<VData>>, 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<RStatement>),
|
||||
List(Vec<RStatement>),
|
||||
Variable(usize, VType, bool),
|
||||
FunctionCall(Arc<RFunction>, Vec<RStatement>),
|
||||
BuiltinFunction(BuiltinFunction, Vec<RStatement>),
|
||||
LibFunction(usize, usize, Vec<RStatement>, VType),
|
||||
Block(RBlock),
|
||||
If(RStatement, RStatement, Option<RStatement>),
|
||||
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<Am<VData>>, 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<Arc<Mutex<VData>>>, 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<Self, ToRunnableError> {
|
||||
if main.inputs.len() != 1 {
|
||||
return Err(ToRunnableError::MainWrongInput);
|
||||
}
|
||||
Ok(Self { main, vars, info })
|
||||
}
|
||||
pub fn run(&self, args: Vec<String>) -> 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)
|
||||
}
|
||||
}
|
15
mers/src/script/global_info.rs
Normal file
15
mers/src/script/global_info.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use crate::libs;
|
||||
|
||||
pub type GSInfo = Arc<GlobalScriptInfo>;
|
||||
// pub type GSMInfo = Arc<Mutex<GlobalScriptInfo>>;
|
||||
pub struct GlobalScriptInfo {
|
||||
pub libs: Vec<libs::Lib>,
|
||||
pub enums: HashMap<String, usize>,
|
||||
}
|
||||
impl GlobalScriptInfo {
|
||||
pub fn to_arc(self) -> GSInfo {
|
||||
Arc::new(self)
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
629
mers/src/script/to_runnable.rs
Normal file
629
mers/src/script/to_runnable.rs
Normal file
@ -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<VType>),
|
||||
WrongArgsForLibFunction(String, Vec<VType>),
|
||||
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<libs::Lib>,
|
||||
pub lib_fns: HashMap<String, (usize, usize)>,
|
||||
pub enum_variants: HashMap<String, usize>,
|
||||
}
|
||||
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<String, usize> {
|
||||
builtins::EVS
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| (v.to_string(), i))
|
||||
.collect()
|
||||
}
|
||||
pub fn new(libs: Vec<libs::Lib>, enum_variants: HashMap<String, usize>) -> 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<String, (usize, VType)>,
|
||||
fns: HashMap<String, Arc<RFunction>>,
|
||||
}
|
||||
|
||||
pub fn to_runnable(s: SFunction, mut ginfo: GInfo) -> Result<RScript, ToRunnableError> {
|
||||
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<usize>,
|
||||
inputs: &mut Vec<VSingleType>,
|
||||
out: &mut Vec<(Vec<VSingleType>, VType)>,
|
||||
) -> Result<(), ToRunnableError> {
|
||||
if s.inputs.len() > inputs.len() {
|
||||
let input_here = &s.inputs[inputs.len()].1;
|
||||
for t in &input_here.types {
|
||||
inputs.push(t.clone().into());
|
||||
get_all_functions(s, ginfo, linfo, input_vars, inputs, out)?;
|
||||
inputs.pop();
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
// set the types
|
||||
for (varid, vartype) in s.inputs.iter().zip(inputs.iter()) {
|
||||
linfo.vars.get_mut(&varid.0).unwrap().1 = vartype.clone().into();
|
||||
}
|
||||
out.push((inputs.clone(), block(&s.block, ginfo, linfo.clone())?.out()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn function(
|
||||
s: &SFunction,
|
||||
ginfo: &mut GInfo,
|
||||
mut linfo: LInfo,
|
||||
) -> Result<RFunction, ToRunnableError> {
|
||||
let mut input_vars = vec![];
|
||||
let mut input_types = vec![];
|
||||
for (iname, itype) in &s.inputs {
|
||||
linfo
|
||||
.vars
|
||||
.insert(iname.clone(), (ginfo.vars, itype.clone()));
|
||||
input_vars.push(ginfo.vars);
|
||||
input_types.push(itype.clone());
|
||||
ginfo.vars += 1;
|
||||
}
|
||||
let mut all_outs = vec![];
|
||||
get_all_functions(
|
||||
s,
|
||||
ginfo,
|
||||
&mut linfo,
|
||||
&input_vars,
|
||||
&mut Vec::with_capacity(s.inputs.len()),
|
||||
&mut all_outs,
|
||||
)?;
|
||||
// set the types to all possible types (get_all_functions sets the types to one single type to get the return type of the block for that case)
|
||||
for (varid, vartype) in s.inputs.iter().zip(input_types.iter()) {
|
||||
linfo.vars.get_mut(&varid.0).unwrap().1 = vartype.clone();
|
||||
}
|
||||
Ok(RFunction {
|
||||
inputs: input_vars,
|
||||
input_types,
|
||||
input_output_map: all_outs,
|
||||
block: block(&s.block, ginfo, linfo)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn block(s: &SBlock, ginfo: &mut GInfo, mut linfo: LInfo) -> Result<RBlock, ToRunnableError> {
|
||||
let mut statements = Vec::new();
|
||||
for st in &s.statements {
|
||||
statements.push(statement(st, ginfo, &mut linfo)?);
|
||||
}
|
||||
Ok(RBlock { statements })
|
||||
}
|
||||
|
||||
fn 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<RStatement, ToRunnableError> {
|
||||
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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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<Self>),
|
||||
EnumVariant(usize, VType),
|
||||
EnumVariantS(String, VType),
|
||||
// CustomType(usize),
|
||||
// CustomTypeS(String),
|
||||
}
|
||||
|
||||
impl VSingleType {
|
||||
@ -299,3 +307,107 @@ impl Into<VType> 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)
|
||||
}
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
// moved to val_data and val_type
|
@ -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()
|
||||
",
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user