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:
mark 2023-06-06 03:09:39 +02:00
parent e3ca8d011d
commit f7feb688e8
16 changed files with 323 additions and 250 deletions

2
mers/Cargo.lock generated
View File

@ -647,7 +647,7 @@ dependencies = [
[[package]]
name = "mers"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"colorize",
"edit",

View File

@ -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()
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(|(i, input)| input.contains(i, info))
.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(

View File

@ -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())
} else {
None
}
})
.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
.fold(VType::empty(), |t, (fn_in, fn_out)| {
if fn_in.len() == (input_types.len())
&& fn_in
.iter()
.zip(input_types.iter())
.all(|(expected, got)| got.contains(expected, info))
.all(|(fn_in, arg)| arg.fits_in(fn_in, info).is_empty())
{
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)
empty = false;
t | fn_out.clone()
} else {
out
t
}
});
if empty {
None
} else {
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) => {

View File

@ -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(&current_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())
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()));
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
[a, [b, c]] := [1, ["str", 12.5]]
a == 1 && b == "str" && c == 12.5

View File

@ -0,0 +1,3 @@
fn plus(a int, b int) -> int { a + b }
10.plus(20) == 30

View 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
View 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
View File

@ -0,0 +1,7 @@
val := !(mers {
"macro returned value"
})
val := !(mers "my_macro.mers")
true

View 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
View File

@ -0,0 +1 @@
true

View File

@ -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!(
assert!(
matches!(
parse::parse(&mut file).unwrap().run(vec![]).inner_cloned(),
VDataEnum::Bool(true)
));
),
"{file_name} didn't return true!"
);
}
}
}

View 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