mirror of
https://github.com/Dummi26/mers.git
synced 2025-06-14 13:06:14 +02:00
750 lines
29 KiB
Rust
Executable File
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(¤t_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)
|
|
}
|