mers/mers_old/src/lang/to_runnable.rs
2023-07-28 00:33:15 +02:00

750 lines
29 KiB
Rust
Executable File

use core::panic;
use std::{
collections::HashMap,
fmt::{Debug, Display},
sync::{Arc, Mutex},
};
use crate::lang::{
global_info::GlobalScriptInfo,
val_data::{VData, VDataEnum},
val_type::{VSingleType, VType},
};
use super::{
builtins::BuiltinFunction,
code_macro::Macro,
code_parsed::{SBlock, SFunction, SStatement, SStatementEnum},
code_runnable::{RBlock, RFunction, RFunctionType, RScript, RStatement, RStatementEnum},
fmtgs::FormatGs,
global_info::GSInfo,
};
pub enum ToRunnableError {
UseOfUndefinedVariable(String),
UseOfUndefinedFunction(String),
UnknownType(String),
CannotDereferenceTypeNTimes(VType, usize, VType),
FunctionWrongArgs(String, Vec<Arc<RFunction>>, Vec<VType>),
InvalidType {
expected: VType,
found: VType,
problematic: VType,
},
CannotAssignTo(VType, 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, form, file) 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 {
self.fmtgs(f, None, &mut super::fmtgs::FormatInfo::default(), None)
}
}
impl FormatGs for ToRunnableError {
fn fmtgs(
&self,
f: &mut std::fmt::Formatter,
info: Option<&GlobalScriptInfo>,
form: &mut super::fmtgs::FormatInfo,
file: Option<&crate::parsing::file::File>,
) -> std::fmt::Result {
match self {
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::UnknownType(name) => write!(f, "Unknown type \"{name}\"."),
Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => {
write!(f, "Cannot dereference type ")?;
og_type.fmtgs(f, info, form, file)?;
write!(f, " {derefs_wanted} times (stopped at ")?;
last_valid_type.fmtgs(f, info, form, file)?;
write!(f, ")")?;
Ok(())
}
Self::FunctionWrongArgs(fn_name, possible_fns, given_types) => {
write!(f, "Wrong args for function \"{fn_name}\": Found (")?;
for (i, t) in given_types.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
t.fmtgs(f, info, form, file)?;
}
write!(f, "), but valid are only ")?;
for (i, func) in possible_fns.iter().enumerate() {
if i != 0 {
if i + 1 == possible_fns.len() {
write!(f, ", and ")?;
} else {
write!(f, ", ")?;
}
}
VDataEnum::Function(Arc::clone(func)).fmtgs(f, info, form, file)?;
}
write!(f, ".")?;
Ok(())
}
Self::InvalidType {
expected,
found,
problematic,
} => {
write!(f, "Invalid type: Expected ")?;
expected.fmtgs(f, info, form, file)?;
write!(f, " but found ")?;
found.fmtgs(f, info, form, file)?;
write!(f, ", which includes ")?;
problematic.fmtgs(f, info, form, file)?;
write!(f, " which is not covered.")?;
Ok(())
}
Self::CaseForceButTypeNotCovered(v) => {
write!(
f,
"Switch! statement, but not all types covered. Types to cover: "
)?;
v.fmtgs(f, info, form, file)?;
Ok(())
}
Self::MatchConditionInvalidReturn(v) => {
write!(f, "match statement condition returned ")?;
v.fmtgs(f, info, form, file)?;
write!(f, ", which is not necessarily a tuple of size 0 to 1.")?;
Ok(())
}
Self::NotIndexableFixed(t, i) => {
write!(f, "Cannot use fixed-index {i} on type ")?;
t.fmtgs(f, info, form, file)?;
write!(f, ".")?;
Ok(())
}
Self::WrongInputsForBuiltinFunction(_builtin, builtin_name, args) => {
write!(
f,
"Wrong arguments for builtin function \"{}\":",
builtin_name
)?;
for arg in args {
write!(f, " ")?;
arg.fmtgs(f, info, form, file)?;
}
write!(f, ".")
}
Self::WrongArgsForLibFunction(name, args) => {
write!(f, "Wrong arguments for library function {}:", name)?;
for arg in args {
write!(f, " ")?;
arg.fmtgs(f, info, form, file)?;
}
write!(f, ".")
}
Self::CannotAssignTo(val, target) => {
write!(f, "Cannot assign type ")?;
val.fmtgs(f, info, form, file)?;
write!(f, " to ")?;
target.fmtgs(f, info, form, file)?;
write!(f, ".")?;
Ok(())
}
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.fmtgs(f, info, form, file)?;
write!(f, ", but its real output type is ")?;
real.fmtgs(f, info, form, file)?;
write!(
f,
", which doesn't fit in the required type because of the problematic types "
)?;
problematic.fmtgs(f, info, form, file)?;
write!(f, ".")?;
Ok(())
}
}
}
}
// Local, used to keep local variables separated
#[derive(Clone)]
struct LInfo {
vars: HashMap<String, Arc<Mutex<(VData, VType)>>>,
fns: HashMap<String, Vec<Arc<RFunction>>>,
}
pub fn to_runnable(
s: SFunction,
mut ginfo: GlobalScriptInfo,
) -> Result<RScript, (ToRunnableError, GSInfo)> {
let func = match function(
&s,
&mut ginfo,
LInfo {
vars: HashMap::new(),
fns: HashMap::new(),
},
) {
Ok(v) => v,
Err(e) => return Err((e, ginfo.to_arc())),
};
let ginfo = ginfo.to_arc();
match RScript::new(func, ginfo.clone()) {
Ok(v) => Ok(v),
Err(e) => Err((e, ginfo)),
}
}
fn function(
s: &SFunction,
ginfo: &mut GlobalScriptInfo,
mut linfo: LInfo,
) -> Result<RFunction, ToRunnableError> {
let mut input_vars = vec![];
let mut input_types = vec![];
for (iname, itype) in &s.inputs {
let mut itype = itype.to_owned();
stypes(&mut itype, ginfo)?;
let var = Arc::new(Mutex::new((VData::new_placeholder(), itype.clone())));
linfo.vars.insert(iname.clone(), Arc::clone(&var));
input_vars.push(var);
input_types.push(itype);
}
// 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(&varid.0).unwrap().lock().unwrap().1 = vartype.clone();
}
let mut o = RFunction {
out_map: vec![],
statement: RFunctionType::Statement(
input_vars,
statement(&s.statement, ginfo, &mut linfo.clone())?,
input_types,
),
};
o.out_map = {
let mut map = vec![];
let mut indices: Vec<_> = if let RFunctionType::Statement(_, _, input_types) = &o.statement
{
input_types.iter().map(|_| 0).collect()
} else {
unreachable!()
};
// like counting: advance first index, when we reach the end, reset to zero and advance the next index, ...
loop {
if let RFunctionType::Statement(_, _, input_types) = &o.statement {
let mut current_types = Vec::with_capacity(input_types.len());
let mut adv = true;
let mut was_last = input_types.is_empty();
for i in 0..input_types.len() {
current_types.push(match input_types[i].types.get(indices[i]) {
Some(v) => v.clone().to(),
None => VType::empty(),
});
if adv {
if indices[i] + 1 < input_types[i].types.len() {
indices[i] += 1;
adv = false;
} else {
indices[i] = 0;
// we just reset the last index back to 0 - if we don't break
// from the loop, we will just start all over again.
if i + 1 == input_types.len() {
was_last = true;
}
}
}
}
let out = o
.out_by_statement(&current_types, &ginfo)
.expect("this should always be a Statement function type");
map.push((current_types, out));
if was_last {
break map;
}
} else {
unreachable!()
}
}
};
Ok(o)
}
fn block(
s: &SBlock,
ginfo: &mut GlobalScriptInfo,
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 })
}
pub fn stypes(t: &mut VType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> {
for t in &mut t.types {
stype(t, ginfo)?;
}
Ok(())
}
pub fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> {
match t {
VSingleType::Any
| VSingleType::Bool
| VSingleType::Int
| VSingleType::Float
| VSingleType::String => (),
VSingleType::Tuple(v) => {
for t in v {
stypes(t, ginfo)?;
}
}
VSingleType::List(t) => stypes(t, ginfo)?,
VSingleType::Reference(t) => stypes(t, ginfo)?,
VSingleType::Thread(t) => 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()
},
)
}
VSingleType::Function(io_map) => {
for io_variant in io_map {
for i in &mut io_variant.0 {
stypes(i, ginfo)?;
}
stypes(&mut io_variant.1, ginfo)?;
}
}
VSingleType::EnumVariant(_, t) => stypes(t, ginfo)?,
VSingleType::CustomTypeS(name) => {
*t = VSingleType::CustomType(
if let Some(v) = ginfo.custom_type_names.get(&name.to_lowercase()) {
*v
} else {
return Err(ToRunnableError::UnknownType(name.to_owned()));
},
)
}
VSingleType::CustomType(_) => (),
}
Ok(())
}
fn statement(
s: &SStatement,
ginfo: &mut GlobalScriptInfo,
linfo: &mut LInfo,
) -> Result<RStatement, ToRunnableError> {
statement_adv(s, ginfo, linfo, &mut None)
}
fn statement_adv(
s: &SStatement,
ginfo: &mut GlobalScriptInfo,
linfo: &mut LInfo,
// if Some((t, is_init)), the statement creates by this function is the left side of an assignment, meaning it can create variables. t is the type that will be assigned to it.
to_be_assigned_to: &mut Option<(VType, &mut bool)>,
) -> Result<RStatement, ToRunnableError> {
// eprintln!("TR : {}", s);
// if let Some(t) = &to_be_assigned_to {
// eprintln!(" --> {}", t.0);
// }
let mut state = match &*s.statement {
SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()).to(),
SStatementEnum::Tuple(v) | SStatementEnum::List(v) => {
let mut w = Vec::with_capacity(v.len());
let mut prev = None;
for (i, v) in v.iter().enumerate() {
if let Some(t) = to_be_assigned_to {
let out_t = if let Some(p) = &prev { p } else { &t.0 };
let inner_t = if let Some(v) = out_t.get_always(i, ginfo) {
v
} else {
panic!("cannot assign: cannot get_always({i}) on type {}.", out_t);
};
let p = std::mem::replace(&mut t.0, inner_t);
if prev.is_none() {
prev = Some(p);
}
};
w.push(statement_adv(v, ginfo, linfo, to_be_assigned_to)?);
}
if let (Some(t), Some(prev)) = (to_be_assigned_to, prev) {
t.0 = prev;
}
if let SStatementEnum::List(_) = &*s.statement {
RStatementEnum::List(w)
} else {
RStatementEnum::Tuple(w)
}
.to()
}
SStatementEnum::Variable(v, is_ref) => {
let existing_var = linfo.vars.get(v);
let is_init_force = if let Some(v) = &to_be_assigned_to {
*v.1
} else {
false
};
// we can't assign to a variable that doesn't exist yet -> create a new one
if is_init_force
|| (existing_var.is_none() && ginfo.to_runnable_automatic_initialization)
{
// if to_be_assigned_to is some (-> this is on the left side of an assignment), create a new variable. else, return an error.
if let Some((t, is_init)) = to_be_assigned_to {
**is_init = true;
#[cfg(not(debug_assertions))]
let var = VData::new_placeholder();
#[cfg(debug_assertions)]
let var = VData::new_placeholder_with_name(v.to_owned());
let var_arc = Arc::new(Mutex::new((var, t.clone())));
linfo.vars.insert(v.to_owned(), Arc::clone(&var_arc));
RStatementEnum::Variable(var_arc, true)
} else {
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
}
} else if let Some(var) = existing_var {
RStatementEnum::Variable(Arc::clone(&var), *is_ref)
} else {
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
}
.to()
}
SStatementEnum::FunctionCall(v, args) => {
let mut rargs = Vec::with_capacity(args.len());
for arg in args.iter() {
rargs.push(statement(arg, ginfo, linfo)?);
}
let arg_types: Vec<_> = rargs.iter().map(|v| v.out(ginfo)).collect();
fn check_fn_args(
arg_types: &Vec<VType>,
func: &RFunction,
ginfo: &GlobalScriptInfo,
) -> bool {
func.out_by_map(arg_types, ginfo).is_some()
// func.inputs.len() == arg_types.len()
// && func
// .inputs
// .iter()
// .zip(arg_types.iter())
// .all(|(fn_in, arg)| arg.fits_in(&fn_in.lock().unwrap().1, ginfo).is_empty())
}
if let Some(funcs) = linfo.fns.get(v) {
'find_func: {
for func in funcs.iter().rev() {
if check_fn_args(&arg_types, &func, ginfo) {
break 'find_func RStatementEnum::FunctionCall(
Arc::clone(&func),
rargs,
);
}
}
return Err(ToRunnableError::FunctionWrongArgs(
v.to_owned(),
funcs.iter().map(|v| Arc::clone(v)).collect(),
arg_types,
));
}
} else {
if let Some(builtin) = BuiltinFunction::get(v) {
let arg_types = rargs.iter().map(|v| v.out(ginfo)).collect();
if builtin.can_take(&arg_types, ginfo) {
RStatementEnum::BuiltinFunctionCall(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 lib = &ginfo.libs[*libid];
let libfn = &lib.registered_fns[*fnid];
let mut empty = true;
let fn_out =
libfn
.1
.iter()
.fold(VType::empty(), |mut t, (fn_in, fn_out)| {
if fn_in.len() == arg_types.len()
&& fn_in.iter().zip(arg_types.iter()).all(|(fn_in, arg)| {
arg.fits_in(fn_in, ginfo).is_empty()
})
{
empty = false;
t.add_typesr(fn_out, ginfo);
}
t
});
if empty {
return Err(ToRunnableError::WrongArgsForLibFunction(
v.to_owned(),
arg_types,
));
}
RStatementEnum::LibFunctionCall(*libid, *fnid, rargs, fn_out.clone())
} else {
return Err(ToRunnableError::UseOfUndefinedFunction(v.clone()));
}
}
}
}
.to(),
SStatementEnum::FunctionDefinition(name, f) => {
let f = Arc::new(function(f, ginfo, linfo.clone())?);
if let Some(name) = name {
// named function => add to global functions
let f = Arc::clone(&f);
if let Some(vec) = linfo.fns.get_mut(name) {
vec.push(f);
} else {
linfo.fns.insert(name.clone(), vec![f]);
}
}
RStatementEnum::Value(VDataEnum::Function(f).to()).to()
}
SStatementEnum::Block(b) => RStatementEnum::Block(block(&b, ginfo, linfo.clone())?).to(),
SStatementEnum::If(c, t, e) => RStatementEnum::If(
{
let condition = statement(&c, ginfo, linfo)?;
let out = condition.out(ginfo).fits_in(
&VType {
types: vec![VSingleType::Bool],
},
ginfo,
);
if out.is_empty() {
condition
} else {
return Err(ToRunnableError::InvalidType {
expected: VSingleType::Bool.into(),
found: condition.out(ginfo),
problematic: VType { types: out },
});
}
},
statement(&t, ginfo, linfo)?,
match e {
Some(v) => Some(statement(&v, ginfo, linfo)?),
None => None,
},
)
.to(),
SStatementEnum::Loop(c) => RStatementEnum::Loop(statement(&c, ginfo, linfo)?).to(),
SStatementEnum::For(v, c, b) => {
let mut linfo = linfo.clone();
let container = statement(&c, ginfo, &mut linfo)?;
let inner = container.out(ginfo).inner_types_for_iters(ginfo);
if inner.types.is_empty() {
return Err(ToRunnableError::ForLoopContainerHasNoInnerTypes);
}
let assign_to = statement_adv(v, ginfo, &mut linfo, &mut Some((inner, &mut true)))?;
let block = statement(&b, ginfo, &mut linfo)?;
let o = RStatementEnum::For(assign_to, container, block);
o.to()
}
SStatementEnum::Switch(switch_on, cases, force) => {
let mut ncases = Vec::with_capacity(cases.len());
let switch_on = statement(switch_on, ginfo, linfo)?;
let og_type = switch_on.out(ginfo);
let mut covered_types = VType::empty();
for (case_type, case_assign_to, case_action) in cases.iter() {
let mut linfo = linfo.clone();
let case_type = {
let mut v = case_type.clone();
stypes(&mut v, ginfo)?;
v
};
covered_types.add_typesr(&case_type, ginfo);
ncases.push((
case_type.clone(),
statement_adv(
case_assign_to,
ginfo,
&mut linfo,
&mut Some((case_type, &mut true)),
)?,
statement(case_action, ginfo, &mut linfo)?,
));
}
if *force {
let types_not_covered = og_type.fits_in(&covered_types, ginfo);
if !types_not_covered.is_empty() {
return Err(ToRunnableError::CaseForceButTypeNotCovered(VType {
types: types_not_covered,
}));
}
}
RStatementEnum::Switch(switch_on, ncases, *force).to()
}
SStatementEnum::Match(cases) => {
let _ncases: Vec<(RStatement, RStatement, RStatement)> =
Vec::with_capacity(cases.len());
let mut ncases = Vec::with_capacity(cases.len());
let mut out_type = VType::empty();
let may_not_match = true;
for (condition, assign_to, action) in cases.iter() {
let mut linfo = linfo.clone();
let condition = statement(condition, ginfo, &mut linfo)?;
let (can_fail, matches) = condition.out(ginfo).matches(ginfo);
let assign_to = statement_adv(
assign_to,
ginfo,
&mut linfo,
&mut Some((matches, &mut true)),
)?;
let action = statement(action, ginfo, &mut linfo)?;
ncases.push((condition, assign_to, action));
if !can_fail {
break;
}
}
if may_not_match {
out_type.add_type(VSingleType::Tuple(vec![]), ginfo);
}
RStatementEnum::Match(ncases).to()
}
SStatementEnum::IndexFixed(st, i) => {
let st = statement(st, ginfo, linfo)?;
if st.out(ginfo).get_always(*i, ginfo).is_some() {
RStatementEnum::IndexFixed(st, *i).to()
} else {
return Err(ToRunnableError::NotIndexableFixed(st.out(ginfo), *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(),
SStatementEnum::TypeDefinition(name, t) => {
// insert to name map has to happen before stypes()
ginfo
.custom_type_names
.insert(name.to_lowercase(), ginfo.custom_types.len());
let mut t = t.to_owned();
stypes(&mut t, ginfo)?;
ginfo.custom_types.push(t);
RStatementEnum::Value(VDataEnum::Tuple(vec![]).to()).to()
}
SStatementEnum::Macro(m) => match m {
Macro::StaticMers(val) => RStatementEnum::Value(val.clone()).to(),
},
};
state.derefs = s.derefs;
// 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 mut force_opt = force_opt.to_owned();
stypes(&mut force_opt, ginfo)?;
let real_output_type = state.out(ginfo);
let problematic_types = real_output_type.fits_in(&force_opt, ginfo);
if problematic_types.is_empty() {
state.force_output_type = Some(force_opt);
} else {
return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types }));
}
}
if let Some((opt, is_init)) = &s.output_to {
// if false, may be changed to true by statement_adv
let mut is_init = *is_init;
let optr = statement_adv(
opt,
ginfo,
linfo,
&mut Some((state.out(ginfo), &mut is_init)),
)?;
state.output_to = Some((Box::new(optr), is_init));
//
// if let Some((var_id, var_out)) = linfo.vars.get(opt) {
// let out = state.out(ginfo);
// 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, ginfo);
// 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));
// state.output_to = Some((ginfo.vars, 0, true));
// ginfo.vars += 1;
// } else {
// // mutate existing variable
// state.output_to = Some((*var_id, *derefs, false));
// }
// } else {
// let mut out = state.out(ginfo);
// for _ in 0..*derefs {
// out = if let Some(v) = out.dereference() {
// v
// } else {
// return Err(ToRunnableError::CannotDereferenceTypeNTimes(
// state.out(ginfo),
// *derefs,
// out,
// ));
// }
// }
// linfo.vars.insert(opt.clone(), (ginfo.vars, out));
// state.output_to = Some((ginfo.vars, *derefs, true));
// ginfo.vars += 1;
// }
}
Ok(state)
}