- 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:
Dummi26 2023-04-25 19:38:00 +02:00
parent 99f8c504a4
commit 89cc7971ee
18 changed files with 1655 additions and 1456 deletions

View File

@ -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")
}
}

View File

@ -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));

View File

@ -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

View File

@ -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(

View 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, "}}")
}
}
}
}

View 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)
}
}

View 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)
}
}

View File

@ -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;

View 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)
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -1 +0,0 @@
// moved to val_data and val_type

View File

@ -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()
",

View File

@ -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:

View File

@ -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;

View File

@ -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)

View File

@ -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
}