mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +01:00
order of operations, tests, VSingleType::Function
- moved && and || in order of operations to make a == b && c == d work as expected - added some .mers files to mers/tests - changed VSingleType::Function (setup for future type system changes)
This commit is contained in:
parent
e3ca8d011d
commit
f7feb688e8
2
mers/Cargo.lock
generated
2
mers/Cargo.lock
generated
@ -647,7 +647,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mers"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"colorize",
|
||||
"edit",
|
||||
|
@ -561,14 +561,26 @@ impl BuiltinFunction {
|
||||
let mut out = VType { types: vec![] };
|
||||
for func in &funcs.types {
|
||||
if let VSingleType::Function(io) = func {
|
||||
for (i, o) in io {
|
||||
if i.iter()
|
||||
.zip(input.iter().skip(1))
|
||||
.all(|(i, input)| input.contains(i, info))
|
||||
let mut empty = true;
|
||||
let fn_out = io.iter().fold(VType::empty(), |t, (fn_in, fn_out)| {
|
||||
if fn_in.len() == (input.len() - 1)
|
||||
&& fn_in
|
||||
.iter()
|
||||
.zip(input.iter().skip(1))
|
||||
.all(|(fn_in, arg)| arg.fits_in(fn_in, info).is_empty())
|
||||
{
|
||||
out = out | o;
|
||||
empty = false;
|
||||
t | fn_out.clone()
|
||||
} else {
|
||||
t
|
||||
}
|
||||
});
|
||||
if empty {
|
||||
unreachable!(
|
||||
"fn args are incorrect for builtin run() or thread()!"
|
||||
);
|
||||
}
|
||||
out = out | fn_out;
|
||||
} else {
|
||||
unreachable!("run called, first arg not a function")
|
||||
}
|
||||
@ -961,7 +973,7 @@ impl BuiltinFunction {
|
||||
}
|
||||
for (i, var) in f.inputs.iter().enumerate() {
|
||||
let val = args[i + 1].run(info).clone_data();
|
||||
*var.lock().unwrap() = val;
|
||||
var.lock().unwrap().0 = val;
|
||||
}
|
||||
f.run(info)
|
||||
} else {
|
||||
@ -977,9 +989,14 @@ impl BuiltinFunction {
|
||||
for (i, var) in f.inputs.iter().enumerate() {
|
||||
let val = args[i + 1].run(info).clone_data();
|
||||
run_input_types.push(val.out_single());
|
||||
*var.lock().unwrap() = val;
|
||||
var.lock().unwrap().0 = val;
|
||||
}
|
||||
let out_type = f.out(&run_input_types, &info);
|
||||
let out_type = f
|
||||
.out(
|
||||
&run_input_types.iter().map(|v| v.clone().into()).collect(),
|
||||
&info,
|
||||
)
|
||||
.unwrap();
|
||||
let info = Arc::clone(info);
|
||||
let f = Arc::clone(f);
|
||||
VDataEnum::Thread(
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
eprintln,
|
||||
assert_eq, eprintln,
|
||||
ops::Deref,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
@ -17,7 +17,7 @@ pub enum RStatementEnum {
|
||||
Value(VData),
|
||||
Tuple(Vec<RStatement>),
|
||||
List(Vec<RStatement>),
|
||||
Variable(Arc<Mutex<VData>>, VType, bool),
|
||||
Variable(Arc<Mutex<(VData, VType)>>, bool),
|
||||
FunctionCall(Arc<RFunction>, Vec<RStatement>),
|
||||
BuiltinFunctionCall(BuiltinFunction, Vec<RStatement>),
|
||||
LibFunctionCall(usize, usize, Vec<RStatement>, VType),
|
||||
@ -60,48 +60,51 @@ impl RBlock {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RFunction {
|
||||
pub inputs: Vec<Arc<Mutex<VData>>>,
|
||||
pub inputs: Vec<Arc<Mutex<(VData, VType)>>>,
|
||||
pub input_types: Vec<VType>,
|
||||
pub input_output_map: Vec<(Vec<VSingleType>, VType)>,
|
||||
pub statement: RStatement,
|
||||
pub out_map: Vec<(Vec<VType>, VType)>,
|
||||
}
|
||||
impl PartialEq for RFunction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
impl Eq for RFunction {
|
||||
fn assert_receiver_is_total_eq(&self) {}
|
||||
}
|
||||
impl RFunction {
|
||||
pub fn run(&self, info: &GSInfo) -> VData {
|
||||
self.statement.run(info)
|
||||
}
|
||||
pub fn out(&self, input_types: &Vec<VSingleType>, info: &GlobalScriptInfo) -> VType {
|
||||
self.input_output_map
|
||||
pub fn out(&self, input_types: &Vec<VType>, info: &GlobalScriptInfo) -> Option<VType> {
|
||||
// NOTE: This can ONLY use self.out_map, because it's used by the VSingleType.fits_in method.
|
||||
let mut empty = true;
|
||||
let out = self
|
||||
.out_map
|
||||
.iter()
|
||||
.find_map(|v| {
|
||||
if v.0 == *input_types {
|
||||
Some(v.1.clone())
|
||||
.fold(VType::empty(), |t, (fn_in, fn_out)| {
|
||||
if fn_in.len() == (input_types.len())
|
||||
&& fn_in
|
||||
.iter()
|
||||
.zip(input_types.iter())
|
||||
.all(|(fn_in, arg)| arg.fits_in(fn_in, info).is_empty())
|
||||
{
|
||||
empty = false;
|
||||
t | fn_out.clone()
|
||||
} else {
|
||||
None
|
||||
t
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| self.statement.out(info))
|
||||
}
|
||||
pub fn out_vt(&self, input_types: &Vec<VType>, info: &GlobalScriptInfo) -> 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, info))
|
||||
{
|
||||
out = out | otype;
|
||||
}
|
||||
}
|
||||
if out.types.is_empty() {
|
||||
// this can happen if we used the `any` type in our function signature,
|
||||
// so in that case we just return the most broad type possible.
|
||||
self.statement.out(info)
|
||||
});
|
||||
if empty {
|
||||
None
|
||||
} else {
|
||||
out
|
||||
Some(out)
|
||||
}
|
||||
}
|
||||
pub fn out_all(&self, info: &GlobalScriptInfo) -> VType {
|
||||
self.statement.out(info)
|
||||
pub fn out_all(&self, _info: &GlobalScriptInfo) -> VType {
|
||||
// self.statement.out(info)
|
||||
self.out_map.iter().fold(VType::empty(), |t, (_, v)| t | v)
|
||||
}
|
||||
pub fn in_types(&self) -> &Vec<VType> {
|
||||
&self.input_types
|
||||
@ -179,16 +182,16 @@ impl RStatementEnum {
|
||||
}
|
||||
VDataEnum::List(out, w).to()
|
||||
}
|
||||
Self::Variable(v, _, is_ref) => {
|
||||
Self::Variable(v, is_ref) => {
|
||||
if *is_ref {
|
||||
VDataEnum::Reference(v.lock().unwrap().clone_mut()).to()
|
||||
VDataEnum::Reference(v.lock().unwrap().0.clone_mut()).to()
|
||||
} else {
|
||||
v.lock().unwrap().clone_data()
|
||||
v.lock().unwrap().0.clone_data()
|
||||
}
|
||||
}
|
||||
Self::FunctionCall(func, args) => {
|
||||
for (i, input) in func.inputs.iter().enumerate() {
|
||||
input.lock().unwrap().assign(args[i].run(info));
|
||||
input.lock().unwrap().0.assign(args[i].run(info));
|
||||
}
|
||||
func.run(info)
|
||||
}
|
||||
@ -333,22 +336,25 @@ impl RStatementEnum {
|
||||
types
|
||||
})
|
||||
.into(),
|
||||
Self::Variable(_, t, is_ref) => {
|
||||
Self::Variable(t, is_ref) => {
|
||||
if *is_ref {
|
||||
VType {
|
||||
types: t
|
||||
.lock()
|
||||
.unwrap()
|
||||
.1
|
||||
.types
|
||||
.iter()
|
||||
.map(|t| VSingleType::Reference(Box::new(t.clone())))
|
||||
.collect(),
|
||||
}
|
||||
} else {
|
||||
t.clone()
|
||||
t.lock().unwrap().1.clone()
|
||||
}
|
||||
}
|
||||
Self::FunctionCall(f, args) => {
|
||||
f.out_vt(&args.iter().map(|v| v.out(info)).collect(), info)
|
||||
}
|
||||
Self::FunctionCall(f, args) => f
|
||||
.out(&args.iter().map(|v| v.out(info)).collect(), info)
|
||||
.expect("invalid args for function -> can't determine output type"),
|
||||
Self::LibFunctionCall(.., out) => out.clone(),
|
||||
Self::Block(b) => b.out(info),
|
||||
Self::If(_, a, b) => {
|
||||
|
@ -4,6 +4,7 @@ use std::{
|
||||
eprintln,
|
||||
fmt::{Debug, Display},
|
||||
sync::{Arc, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -198,7 +199,7 @@ impl FormatGs for ToRunnableError {
|
||||
// Local, used to keep local variables separated
|
||||
#[derive(Clone)]
|
||||
struct LInfo {
|
||||
vars: HashMap<String, (Arc<Mutex<VData>>, VType)>,
|
||||
vars: HashMap<String, Arc<Mutex<(VData, VType)>>>,
|
||||
fns: HashMap<String, Vec<Arc<RFunction>>>,
|
||||
}
|
||||
|
||||
@ -206,17 +207,6 @@ pub fn to_runnable(
|
||||
s: SFunction,
|
||||
mut ginfo: GlobalScriptInfo,
|
||||
) -> Result<RScript, (ToRunnableError, GSInfo)> {
|
||||
if s.inputs.len() != 1 || s.inputs[0].0 != "args" {
|
||||
return Err((ToRunnableError::MainWrongInput, ginfo.to_arc()));
|
||||
}
|
||||
assert_eq!(
|
||||
s.inputs[0].1,
|
||||
VType {
|
||||
types: vec![VSingleType::List(VType {
|
||||
types: vec![VSingleType::String],
|
||||
})],
|
||||
}
|
||||
);
|
||||
let func = match function(
|
||||
&s,
|
||||
&mut ginfo,
|
||||
@ -235,44 +225,6 @@ pub fn to_runnable(
|
||||
}
|
||||
}
|
||||
|
||||
// go over every possible known-type input for the given function, returning all possible RFunctions.
|
||||
fn get_all_functions(
|
||||
s: &SFunction,
|
||||
ginfo: &mut GlobalScriptInfo,
|
||||
linfo: &mut LInfo,
|
||||
input_vars: &Vec<Arc<Mutex<VData>>>,
|
||||
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 {
|
||||
let mut t = t.clone();
|
||||
stype(&mut t, ginfo)?;
|
||||
inputs.push(t);
|
||||
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 = {
|
||||
let mut vartype = vartype.clone();
|
||||
stype(&mut vartype, ginfo)?;
|
||||
vartype.to()
|
||||
}
|
||||
}
|
||||
// the statement is parsed multiple times (this is why we get duplicates in stderr):
|
||||
// - n times for the function args to generate the input-output map
|
||||
// - 1 more time here, where the function args aren't single types
|
||||
out.push((
|
||||
inputs.clone(),
|
||||
statement(&s.statement, ginfo, &mut linfo.clone())?.out(ginfo),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn function(
|
||||
s: &SFunction,
|
||||
ginfo: &mut GlobalScriptInfo,
|
||||
@ -283,32 +235,74 @@ fn function(
|
||||
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()));
|
||||
linfo
|
||||
.vars
|
||||
.insert(iname.clone(), (Arc::clone(&var), itype.clone()));
|
||||
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);
|
||||
}
|
||||
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();
|
||||
linfo.vars.get(&varid.0).unwrap().lock().unwrap().1 = vartype.clone();
|
||||
}
|
||||
Ok(RFunction {
|
||||
let mut o = RFunction {
|
||||
out_map: vec![],
|
||||
inputs: input_vars,
|
||||
input_types,
|
||||
input_output_map: all_outs,
|
||||
statement: statement(&s.statement, ginfo, &mut linfo.clone())?,
|
||||
})
|
||||
};
|
||||
o.out_map = {
|
||||
let mut map = vec![];
|
||||
let mut indices: Vec<_> = o.input_types.iter().map(|_| 0).collect();
|
||||
// like counting: advance first index, when we reach the end, reset to zero and advance the next index, ...
|
||||
loop {
|
||||
let mut current_types = Vec::with_capacity(o.input_types.len());
|
||||
let mut adv = true;
|
||||
let mut was_last = o.input_types.is_empty();
|
||||
for i in 0..o.input_types.len() {
|
||||
current_types.push(match o.input_types[i].types.get(indices[i]) {
|
||||
Some(v) => v.clone().to(),
|
||||
None => VType::empty(),
|
||||
});
|
||||
if adv {
|
||||
if indices[i] + 1 < o.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 == o.input_types.len() {
|
||||
was_last = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// let out = o.out(¤t_types, ginfo).expect("invalid args????");
|
||||
let out = {
|
||||
let mut actual = Vec::with_capacity(o.inputs.len());
|
||||
// simulate these variable types
|
||||
for (fn_input, c_type) in o.inputs.iter().zip(current_types.iter()) {
|
||||
actual.push(std::mem::replace(
|
||||
&mut fn_input.lock().unwrap().1,
|
||||
c_type.clone(),
|
||||
));
|
||||
}
|
||||
// not get the return type if these were the actual types
|
||||
let out = o.statement.out(ginfo);
|
||||
// reset
|
||||
for (fn_input, actual) in o.inputs.iter().zip(actual) {
|
||||
std::mem::replace(&mut fn_input.lock().unwrap().1, actual);
|
||||
}
|
||||
// return
|
||||
out
|
||||
};
|
||||
map.push((current_types, out));
|
||||
if was_last {
|
||||
break map;
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(o)
|
||||
}
|
||||
|
||||
fn block(
|
||||
@ -364,7 +358,7 @@ pub fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), To
|
||||
VSingleType::Function(io_map) => {
|
||||
for io_variant in io_map {
|
||||
for i in &mut io_variant.0 {
|
||||
stype(i, ginfo)?;
|
||||
stypes(i, ginfo)?;
|
||||
}
|
||||
stypes(&mut io_variant.1, ginfo)?;
|
||||
}
|
||||
@ -449,24 +443,14 @@ fn statement_adv(
|
||||
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));
|
||||
linfo
|
||||
.vars
|
||||
.insert(v.to_owned(), (Arc::clone(&var_arc), t.clone()));
|
||||
RStatementEnum::Variable(var_arc, t.clone(), true)
|
||||
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.0),
|
||||
{
|
||||
let mut v = var.1.clone();
|
||||
stypes(&mut v, ginfo)?;
|
||||
v
|
||||
},
|
||||
*is_ref,
|
||||
)
|
||||
RStatementEnum::Variable(Arc::clone(&var), *is_ref)
|
||||
} else {
|
||||
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
|
||||
}
|
||||
@ -477,45 +461,23 @@ fn statement_adv(
|
||||
for arg in args.iter() {
|
||||
rargs.push(statement(arg, ginfo, linfo)?);
|
||||
}
|
||||
fn check_fn_args(
|
||||
args: &Vec<VType>,
|
||||
inputs: &Vec<(Vec<VType>, VType)>,
|
||||
ginfo: &GlobalScriptInfo,
|
||||
) -> Option<VType> {
|
||||
let mut fit_any = false;
|
||||
let mut out = VType::empty();
|
||||
for (inputs, output) in inputs {
|
||||
if args.len() == inputs.len()
|
||||
&& args
|
||||
.iter()
|
||||
.zip(inputs.iter())
|
||||
.all(|(arg, input)| arg.fits_in(input, ginfo).is_empty())
|
||||
{
|
||||
fit_any = true;
|
||||
out = out | output;
|
||||
}
|
||||
}
|
||||
if fit_any {
|
||||
Some(out)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
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.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 let Some(_out) = check_fn_args(
|
||||
&arg_types,
|
||||
&func
|
||||
.input_output_map
|
||||
.iter()
|
||||
.map(|v| {
|
||||
(v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned())
|
||||
})
|
||||
.collect(),
|
||||
ginfo,
|
||||
) {
|
||||
if check_fn_args(&arg_types, &func, ginfo) {
|
||||
break 'find_func RStatementEnum::FunctionCall(
|
||||
Arc::clone(&func),
|
||||
rargs,
|
||||
@ -545,14 +507,27 @@ fn statement_adv(
|
||||
if let Some((libid, fnid)) = ginfo.lib_fns.get(v) {
|
||||
let lib = &ginfo.libs[*libid];
|
||||
let libfn = &lib.registered_fns[*fnid];
|
||||
if let Some(fn_out) = check_fn_args(&arg_types, &libfn.1, ginfo) {
|
||||
RStatementEnum::LibFunctionCall(*libid, *fnid, rargs, fn_out.clone())
|
||||
} else {
|
||||
let mut empty = true;
|
||||
let fn_out = libfn.1.iter().fold(VType::empty(), |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 | fn_out.clone()
|
||||
} else {
|
||||
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()));
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ impl VDataEnum {
|
||||
Self::String(..) => VSingleType::String,
|
||||
Self::Tuple(v) => VSingleType::Tuple(v.iter().map(|v| v.out_single().to()).collect()),
|
||||
Self::List(t, _) => VSingleType::List(t.clone()),
|
||||
Self::Function(f) => VSingleType::Function(f.input_output_map.clone()),
|
||||
Self::Function(f) => VSingleType::Function(f.out_map.clone()),
|
||||
Self::Thread(_, o) => VSingleType::Thread(o.clone()),
|
||||
Self::Reference(r) => VSingleType::Reference(Box::new(r.out_single())),
|
||||
Self::EnumVariant(e, v) => VSingleType::EnumVariant(*e, v.out_single().to()),
|
||||
@ -560,7 +560,7 @@ impl FormatGs for VDataEnum {
|
||||
write!(f, "...]")
|
||||
}
|
||||
Self::Function(func) => {
|
||||
VSingleType::Function(func.input_output_map.clone()).fmtgs(f, info, form, file)
|
||||
VSingleType::Function(func.out_map.clone()).fmtgs(f, info, form, file)
|
||||
}
|
||||
Self::Thread(..) => write!(f, "[TODO] THREAD"),
|
||||
Self::Reference(inner) => {
|
||||
|
@ -2,11 +2,14 @@ use std::{
|
||||
collections::HashMap,
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
ops::BitOr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use super::{
|
||||
code_runnable::{RFunction, RStatementEnum},
|
||||
fmtgs::FormatGs,
|
||||
global_info::{self, GSInfo, GlobalScriptInfo},
|
||||
val_data::VDataEnum,
|
||||
};
|
||||
|
||||
use super::global_info::LogMsg;
|
||||
@ -25,7 +28,7 @@ pub enum VSingleType {
|
||||
String,
|
||||
Tuple(Vec<VType>),
|
||||
List(VType),
|
||||
Function(Vec<(Vec<VSingleType>, VType)>),
|
||||
Function(Vec<(Vec<VType>, VType)>),
|
||||
Thread(VType),
|
||||
Reference(Box<Self>),
|
||||
EnumVariant(usize, VType),
|
||||
@ -467,18 +470,24 @@ impl VSingleType {
|
||||
(Self::Tuple(_), _) => false,
|
||||
(Self::List(a), Self::List(b)) => a.fits_in(b, info).is_empty(),
|
||||
(Self::List(_), _) => false,
|
||||
(Self::Function(a), Self::Function(b)) => 'func_out: {
|
||||
for a in a {
|
||||
'search: {
|
||||
for b in b {
|
||||
if a.1.fits_in(&b.1, info).is_empty()
|
||||
&& a.0.len() == b.0.len()
|
||||
&& a.0.iter().zip(b.0.iter()).all(|(a, b)| *a == *b)
|
||||
{
|
||||
break 'search;
|
||||
}
|
||||
(Self::Function(a), Self::Function(b)) => 'fnt: {
|
||||
// since RFunction.out only uses out_map, we can create a dummy RFunction here.
|
||||
let af = RFunction {
|
||||
inputs: vec![],
|
||||
input_types: vec![],
|
||||
statement: RStatementEnum::Value(VDataEnum::Bool(false).to()).to(),
|
||||
out_map: a.clone(),
|
||||
};
|
||||
for (ins, out) in b {
|
||||
// try everything that would be valid for b
|
||||
if let Some(v) = af.out(ins, info) {
|
||||
if !v.fits_in(out, info).is_empty() {
|
||||
// found something that's valid for both, but a returns something that doesn't fit in what b would have returned -> a doesn't fit.
|
||||
break 'fnt false;
|
||||
}
|
||||
break 'func_out false;
|
||||
} else {
|
||||
// found something that's valid for b but not for a -> a doesn't fit.
|
||||
break 'fnt false;
|
||||
}
|
||||
}
|
||||
true
|
||||
|
@ -192,7 +192,7 @@ pub fn parse_step_interpret(
|
||||
"args".to_string(),
|
||||
VSingleType::List(VSingleType::String.into()).to(),
|
||||
)],
|
||||
SStatementEnum::Block(parse_block_advanced(file, Some(false), false, true, false)?).to(),
|
||||
SStatementEnum::Block(parse_block_advanced(file, Some(false), true, true, false)?).to(),
|
||||
);
|
||||
if ginfo.log.after_parse.log() {
|
||||
ginfo.log.log(LogMsg::AfterParse(
|
||||
@ -855,6 +855,7 @@ pub mod implementation {
|
||||
file.skip_whitespaces();
|
||||
// least local (evaluated last)
|
||||
// 0 =
|
||||
// 015 && ||
|
||||
// 020 == !=
|
||||
// 025 > < >= <=
|
||||
// 050 + -
|
||||
@ -991,35 +992,6 @@ pub mod implementation {
|
||||
)
|
||||
.to()
|
||||
}
|
||||
// 023 && ||
|
||||
(0..=23, Some('&'))
|
||||
if matches!(
|
||||
file.get_char(file.get_pos().current_char_index + 1),
|
||||
Some('&')
|
||||
) =>
|
||||
{
|
||||
file.next();
|
||||
file.next();
|
||||
SStatementEnum::FunctionCall(
|
||||
"and".to_owned(),
|
||||
vec![out, parse_statement_adv(file, false, 24)?],
|
||||
)
|
||||
.to()
|
||||
}
|
||||
(0..=23, Some('|'))
|
||||
if matches!(
|
||||
file.get_char(file.get_pos().current_char_index + 1),
|
||||
Some('|')
|
||||
) =>
|
||||
{
|
||||
file.next();
|
||||
file.next();
|
||||
SStatementEnum::FunctionCall(
|
||||
"or".to_owned(),
|
||||
vec![out, parse_statement_adv(file, false, 24)?],
|
||||
)
|
||||
.to()
|
||||
}
|
||||
// 020 == !=
|
||||
(0..=20, Some('='))
|
||||
if matches!(
|
||||
@ -1049,6 +1021,35 @@ pub mod implementation {
|
||||
)
|
||||
.to()
|
||||
}
|
||||
// 015 && ||
|
||||
(0..=15, Some('&'))
|
||||
if matches!(
|
||||
file.get_char(file.get_pos().current_char_index + 1),
|
||||
Some('&')
|
||||
) =>
|
||||
{
|
||||
file.next();
|
||||
file.next();
|
||||
SStatementEnum::FunctionCall(
|
||||
"and".to_owned(),
|
||||
vec![out, parse_statement_adv(file, false, 16)?],
|
||||
)
|
||||
.to()
|
||||
}
|
||||
(0..=15, Some('|'))
|
||||
if matches!(
|
||||
file.get_char(file.get_pos().current_char_index + 1),
|
||||
Some('|')
|
||||
) =>
|
||||
{
|
||||
file.next();
|
||||
file.next();
|
||||
SStatementEnum::FunctionCall(
|
||||
"or".to_owned(),
|
||||
vec![out, parse_statement_adv(file, false, 16)?],
|
||||
)
|
||||
.to()
|
||||
}
|
||||
// 000 = :=
|
||||
(0..=0, Some('=')) => {
|
||||
file.next();
|
||||
@ -1145,6 +1146,7 @@ pub mod implementation {
|
||||
file.next();
|
||||
break;
|
||||
}
|
||||
Some(']') => break,
|
||||
Some(ch) if ch.is_whitespace() => break,
|
||||
Some(ch) if ch == ',' => {
|
||||
file.next();
|
||||
@ -1233,6 +1235,7 @@ pub mod implementation {
|
||||
match file.next() {
|
||||
Some('(') => {
|
||||
break 'parse_single_type if name.as_str() == "fn" {
|
||||
// syntax: fn((arg1 arg2 arg3) returns1 (arg1 arg2) returns2)
|
||||
let mut fn_types = vec![];
|
||||
loop {
|
||||
file.skip_whitespaces();
|
||||
@ -1240,6 +1243,7 @@ pub mod implementation {
|
||||
Some('(') => {
|
||||
let mut args = vec![];
|
||||
loop {
|
||||
file.skip_whitespaces();
|
||||
let (t, fn_args_closed) =
|
||||
parse_type_adv(file, true)?;
|
||||
args.push(t);
|
||||
@ -1247,39 +1251,9 @@ pub mod implementation {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let out = if let Some(v) = args.pop() {
|
||||
v
|
||||
} else {
|
||||
VSingleType::Tuple(vec![]).to()
|
||||
};
|
||||
fn get_all_single_types(
|
||||
types: &mut Vec<VType>,
|
||||
) -> Vec<Vec<VSingleType>>
|
||||
{
|
||||
if types.is_empty() {
|
||||
vec![]
|
||||
} else if types.len() == 1 {
|
||||
vec![types[0].types.clone()]
|
||||
} else {
|
||||
let last = types.pop().unwrap();
|
||||
let o = get_all_single_types(types);
|
||||
let mut out = Vec::with_capacity(
|
||||
o.len() * last.types.len(),
|
||||
);
|
||||
for other in o {
|
||||
for t in &last.types {
|
||||
let mut vec = other.clone();
|
||||
vec.push(t.clone());
|
||||
out.push(vec);
|
||||
}
|
||||
}
|
||||
types.push(last);
|
||||
out
|
||||
}
|
||||
}
|
||||
for t in get_all_single_types(&mut args) {
|
||||
fn_types.push((t, out.clone()));
|
||||
}
|
||||
file.skip_whitespaces();
|
||||
let out = parse_type(file)?;
|
||||
fn_types.push((args, out));
|
||||
}
|
||||
Some(')') => break,
|
||||
Some(other) => {
|
||||
|
3
mers/tests/destructuring_assignment.mers
Normal file
3
mers/tests/destructuring_assignment.mers
Normal file
@ -0,0 +1,3 @@
|
||||
[a, [b, c]] := [1, ["str", 12.5]]
|
||||
|
||||
a == 1 && b == "str" && c == 12.5
|
3
mers/tests/force_output_type.mers
Normal file
3
mers/tests/force_output_type.mers
Normal file
@ -0,0 +1,3 @@
|
||||
fn plus(a int, b int) -> int { a + b }
|
||||
|
||||
10.plus(20) == 30
|
18
mers/tests/functions_double_definitions.mers
Normal file
18
mers/tests/functions_double_definitions.mers
Normal file
@ -0,0 +1,18 @@
|
||||
type person [string int]
|
||||
fn name(p person/&person) p.0
|
||||
fn age(p person/&person) p.1
|
||||
|
||||
type village [[float float] string]
|
||||
fn name(v village/&village) v.1
|
||||
fn location(v village/&village) v.0
|
||||
fn x_coordinate(v village/&village) v.0.0
|
||||
fn y_coordinate(v village/&village) v.0.1
|
||||
|
||||
|
||||
|
||||
customer := ["Max M.", 43]
|
||||
home_town := [[12.3, 5.09], "Maxburg"]
|
||||
|
||||
customer.name() == "Max M."
|
||||
&& home_town.name() == "Maxburg"
|
||||
&& home_town.location() == [12.3, 5.09]
|
10
mers/tests/get_ref.mers
Normal file
10
mers/tests/get_ref.mers
Normal file
@ -0,0 +1,10 @@
|
||||
list := [1 2 3 4 5 6 7 8 9 ...]
|
||||
|
||||
// calling get on an &list will get a reference
|
||||
&list.get(2).assume1() = 24
|
||||
// calling get on a list will get a value
|
||||
should_not_be_changeable := list.get(3).assume1()
|
||||
&should_not_be_changeable = 24
|
||||
|
||||
list.get(2) == [24]
|
||||
&& list.get(3) != [24]
|
7
mers/tests/macro.mers
Normal file
7
mers/tests/macro.mers
Normal file
@ -0,0 +1,7 @@
|
||||
val := !(mers {
|
||||
"macro returned value"
|
||||
})
|
||||
|
||||
val := !(mers "my_macro.mers")
|
||||
|
||||
true
|
9
mers/tests/modify_variable.mers
Normal file
9
mers/tests/modify_variable.mers
Normal file
@ -0,0 +1,9 @@
|
||||
// NOTE: Might change, but this is the current state of things
|
||||
x := 10
|
||||
t := thread(() {
|
||||
sleep(0.25)
|
||||
x
|
||||
})
|
||||
&x = 20 // -> 20 20 because it modifies the original variable x
|
||||
// x := 20 // -> 10 20 because it shadows the original variable x
|
||||
t.await() == x
|
1
mers/tests/my_macro.mers
Normal file
1
mers/tests/my_macro.mers
Normal file
@ -0,0 +1 @@
|
||||
true
|
@ -14,10 +14,13 @@ fn run_all() {
|
||||
eprintln!("Checking {}", file_name);
|
||||
let mut file = File::new(fs::read_to_string(file.path()).unwrap(), file.path());
|
||||
// has to return true, otherwise the test will fail
|
||||
assert!(matches!(
|
||||
parse::parse(&mut file).unwrap().run(vec![]).inner_cloned(),
|
||||
VDataEnum::Bool(true)
|
||||
));
|
||||
assert!(
|
||||
matches!(
|
||||
parse::parse(&mut file).unwrap().run(vec![]).inner_cloned(),
|
||||
VDataEnum::Bool(true)
|
||||
),
|
||||
"{file_name} didn't return true!"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
mers/tests/thread_NOCHECK_ONLY_COMPILE.mers
Normal file
38
mers/tests/thread_NOCHECK_ONLY_COMPILE.mers
Normal file
@ -0,0 +1,38 @@
|
||||
// this is true by default so the example doesn't finish too quickly or too slowly depending on your hardware.
|
||||
// you can set it to false and tweak the max value for a more authentic cpu-heavy workload.
|
||||
fake_delay := true
|
||||
|
||||
// this will be shared between the two threads to report the progress in percent (0-100%).
|
||||
progress := 0
|
||||
|
||||
// an anonymous function that sums all numbers from 0 to max.
|
||||
// it captures the progress variable and uses it to report its status to the main thread, which will periodically print the current progress.
|
||||
// once done, it returns a string with the sum of all numbers.
|
||||
calculator := (max int) {
|
||||
sum := 0
|
||||
for i max {
|
||||
i := i + 1
|
||||
// println("i: {0} s: {1}".format(i.to_string() sum.to_string()))
|
||||
&sum = sum + i
|
||||
// if fake_delay sleep(1)
|
||||
&progress = i * 100 / max
|
||||
}
|
||||
"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.
|
||||
slow_calculation_thread := calculator.thread(if fake_delay 10 else 20000000)
|
||||
|
||||
// every second, print the progress. once it reaches 100%, stop
|
||||
loop {
|
||||
sleep(1)
|
||||
println("{0}%".format(progress.to_string()))
|
||||
progress == 100 // break from the loop
|
||||
}
|
||||
|
||||
// use await() to get the result from the thread. if the thread is still running, this will block until the thread finishes.
|
||||
result := slow_calculation_thread.await()
|
||||
|
||||
println("Thread finished, result: {0}".format(result))
|
||||
|
||||
true
|
Loading…
Reference in New Issue
Block a user