changed assignment parser from var = statement to statement = statement

left side of assignments can now be various different things instead of just variables. any statement that returns a reference can be used to assign to the value behind that reference. variables are automatically referenced, so the '&' can be omitted. if the variable contains a reference and that reference should be used, dereference it with *varname instead of just varname.
This commit is contained in:
mark 2023-05-12 00:44:47 +02:00
parent 6f31abd5cc
commit bba48b311f
18 changed files with 348 additions and 204 deletions

19
amogus.mers Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,10 @@
list = [1 2 3 4 5 6 7 8 9 ...] list = [1 2 3 4 5 6 7 8 9 ...]
second = &list.get_ref(2).assume1() // second = &list.get_ref(2).assume1()
second.debug() // second.debug()
*second = 24 // *second = 24
second.debug() // second.debug()
&list.get_ref(2).assume1() = 24
list.debug() list.debug()

View File

@ -12,6 +12,6 @@ sleep(0.5)
http_get("https:\//github.com/").assume_no_enum() http_get("https:\//github.com/").assume_no_enum()
println("got github start page as html") println("got github start page as html")
// t.await() t.await()
http_get("not a url").debug() http_get("not a url").debug()

View File

@ -19,3 +19,21 @@ fn map_string_to_int(list [string ...] func fn((string int))) {
for v ["1" "2" "5" "-10" ...].map_string_to_int((s string) s.parse_int().assume1("list contained strings that can't be parsed to an int!")) { for v ["1" "2" "5" "-10" ...].map_string_to_int((s string) s.parse_int().assume1("list contained strings that can't be parsed to an int!")) {
debug(v) debug(v)
} }
// returns an iterator to iteratively compute square numbers
// using the fact that (n+1)^2 = n^2 + 2n + 1
fn square_numbers() {
i = 0
val = 0
() {
val = val + { 2 * i } + 1
i = i + 1
[val]
}
}
for n square_numbers() {
println(n.to_string())
n >= 100
}

4
mers/a.mers Normal file
View File

@ -0,0 +1,4 @@
a = 1
b = 2
a.debug()
b.debug()

View File

@ -128,6 +128,8 @@ fn normal_main() {
}; };
match parsing::parse::parse(&mut file) { match parsing::parse::parse(&mut file) {
Ok(script) => { Ok(script) => {
#[cfg(debug_assertions)]
eprintln!("{script:#?}");
println!(" - - - - -"); println!(" - - - - -");
let start = Instant::now(); let start = Instant::now();
let out = script.run(std::env::args().skip(2).collect()); let out = script.run(std::env::args().skip(2).collect());

View File

@ -120,10 +120,7 @@ impl Plugin for MersNuPlugin {
} }
Err(e) => Value::Error { Err(e) => Value::Error {
error: Box::new(ShellError::IncorrectValue { error: Box::new(ShellError::IncorrectValue {
msg: format!( msg: format!("Couldn't compile mers, error: {}", e.with_file(&file)),
"Couldn't compile mers, error: {}",
e.with_file(&file)
),
span: source_span, span: source_span,
}), }),
}, },

View File

@ -148,6 +148,9 @@ pub fn parse(file: &mut File) -> Result<RScript, Error> {
Err(e) => return Err((e.into(), ginfo.to_arc()).into()), Err(e) => return Err((e.into(), ginfo.to_arc()).into()),
}; };
#[cfg(debug_assertions)]
eprintln!("{func:#?}");
ginfo.libs = match parse_step_libs_load(libs, &mut ginfo) { ginfo.libs = match parse_step_libs_load(libs, &mut ginfo) {
Ok(v) => v, Ok(v) => v,
Err(e) => return Err((e.into(), ginfo.to_arc()).into()), Err(e) => return Err((e.into(), ginfo.to_arc()).into()),
@ -533,11 +536,12 @@ pub mod implementation {
} }
pub(crate) fn parse_statement(file: &mut File) -> Result<SStatement, ParseError> { pub(crate) fn parse_statement(file: &mut File) -> Result<SStatement, ParseError> {
parse_statement_adv(file, false) parse_statement_adv(file, false, 0)
} }
pub(crate) fn parse_statement_adv( pub(crate) fn parse_statement_adv(
file: &mut File, file: &mut File,
is_part_of_chain_already: bool, is_part_of_chain_already: bool,
chain_level: usize,
) -> Result<SStatement, ParseError> { ) -> Result<SStatement, ParseError> {
file.skip_whitespaces(); file.skip_whitespaces();
let err_start_of_statement = *file.get_pos(); let err_start_of_statement = *file.get_pos();
@ -589,37 +593,6 @@ pub mod implementation {
}; };
match nchar { match nchar {
Some(':') => { Some(':') => {
let file_pos_before_pot_type = *file.get_pos();
let parsed_type = parse_type(file);
file.skip_whitespaces();
if let Some('=') = file.next() {
let err_equals_sign = *file.get_pos();
let start = start.trim();
let derefs = start.chars().take_while(|c| *c == '*').count();
match parse_statement(file) {
Ok(v) => break v
.output_to(start[derefs..].to_owned(), derefs)
.force_output_type(Some(match parsed_type {
Ok(v) => v,
Err(mut e) => {
e.context.push((
format!("interpreted this as an assignment to a variable with the format <var>::<var_type> = <statement>"), Some((err_start_of_statement, Some(err_equals_sign)))
));
return Err(e);
}
})),
Err(mut e) => {
e.context.push((
format!(
"statement was supposed to be assigned to variable {start}"
),
Some((err_start_of_statement, Some(err_equals_sign))),
));
return Err(e);
}
}
}
file.set_pos(file_pos_before_pot_type);
return Ok(SStatement::new(SStatementEnum::EnumVariant( return Ok(SStatement::new(SStatementEnum::EnumVariant(
start, start,
parse_statement(file)?, parse_statement(file)?,
@ -636,25 +609,6 @@ pub mod implementation {
}); });
} }
file.skip_whitespaces(); file.skip_whitespaces();
// var = statement
if let Some('=') = file.peek() {
file.next();
let err_equals_sign = *file.get_pos();
let start = start.trim();
let derefs = start.chars().take_while(|c| *c == '*').count();
match parse_statement(file) {
Ok(v) => break v.output_to(start[derefs..].to_owned(), derefs),
Err(mut e) => {
e.context.push((
format!(
"statement was supposed to be assigned to variable {start}"
),
Some((err_start_of_statement, Some(err_equals_sign))),
));
return Err(e);
}
};
}
// parse normal statement // parse normal statement
let start = start.trim(); let start = start.trim();
match start { match start {
@ -889,23 +843,21 @@ pub mod implementation {
} }
}; };
let err_end_of_original_statement = *file.get_pos(); let err_end_of_original_statement = *file.get_pos();
// special characters that can follow a statement (loop because these can be chained)
loop {
file.skip_whitespaces(); file.skip_whitespaces();
if !file[file.get_pos().current_char_index..].starts_with("..") { out = match (chain_level, file.peek()) {
// dot chain syntax only works if there is only one dot (0..=200, Some('.'))
if let Some('.') = file.peek() { if !matches!(
// consume the dot (otherwise, a.b.c syntax will break in certain cases) file.get_char(file.get_pos().current_char_index + 1),
file.next(); Some('.')
} ) =>
if !is_part_of_chain_already {
let mut chain_length = 0;
let mut err_end_of_prev = err_end_of_original_statement;
while let Some('.') =
file.get_char(file.get_pos().current_char_index.saturating_sub(1))
{ {
file.next();
let err_start_of_wrapper = *file.get_pos(); let err_start_of_wrapper = *file.get_pos();
let wrapper = parse_statement_adv(file, true)?; let wrapper = parse_statement_adv(file, true, 250)?;
let err_end_of_wrapper = *file.get_pos(); let err_end_of_wrapper = *file.get_pos();
out = match *wrapper.statement { match *wrapper.statement {
SStatementEnum::FunctionCall(func, args) => { SStatementEnum::FunctionCall(func, args) => {
let args = [out].into_iter().chain(args.into_iter()).collect(); let args = [out].into_iter().chain(args.into_iter()).collect();
SStatementEnum::FunctionCall(func, args).to() SStatementEnum::FunctionCall(func, args).to()
@ -913,67 +865,131 @@ pub mod implementation {
SStatementEnum::Value(vd) => match &vd.data().0 { SStatementEnum::Value(vd) => match &vd.data().0 {
VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, *i as _).to(), VDataEnum::Int(i) => SStatementEnum::IndexFixed(out, *i as _).to(),
_ => { _ => {
let mut context = vec![];
if chain_length > 0 {
context.push((
format!(
"this is the {} wrapping statement in a chain.",
match chain_length {
1 => format!("second"),
2 => format!("third"),
// NOTE: this technically breaks at 21, but I don't care.
len => format!("{}th", len + 1),
}
),
None,
));
}
context.push((
format!("the statement that was supposed to be wrapped"),
Some((err_start_of_statement, Some(err_end_of_prev))),
));
return Err(ParseError { return Err(ParseError {
err: ParseErrors::CannotUseFixedIndexingWithThisType(vd.out()), err: ParseErrors::CannotUseFixedIndexingWithThisType(vd.out()),
location: err_start_of_wrapper, location: err_start_of_wrapper,
location_end: Some(err_end_of_wrapper), location_end: Some(err_end_of_wrapper),
context, context: vec![(
format!("this is a wrapping statement (a.f(), a.0, etc.).",),
None,
)],
info: None, info: None,
}); });
} }
}, },
other => { other => {
let mut context = vec![];
if chain_length > 0 {
context.push((
format!(
"this is the {} wrapping statement in a chain.",
match chain_length {
1 => format!("second"),
2 => format!("third"),
// NOTE: this technically breaks at 21, but I don't care.
len => format!("{}th", len + 1),
}
),
None,
));
}
context.push((
format!("the statement that was supposed to be wrapped"),
Some((err_start_of_statement, Some(err_end_of_prev))),
));
return Err(ParseError { return Err(ParseError {
err: ParseErrors::CannotWrapWithThisStatement(other), err: ParseErrors::CannotWrapWithThisStatement(other),
location: err_start_of_wrapper, location: err_start_of_wrapper,
location_end: Some(err_end_of_wrapper), location_end: Some(err_end_of_wrapper),
context, context: vec![(
format!("this is a wrapping statement (a.f(), a.0, etc.).",),
None,
)],
info: None, info: None,
}); });
} }
}
}
(0..=100, Some('+')) => {
file.next();
SStatementEnum::FunctionCall(
"add".to_owned(),
// AMONG
vec![out, parse_statement_adv(file, true, 100)?],
)
.to()
}
(0..=100, Some('-')) => {
file.next();
SStatementEnum::FunctionCall(
"sub".to_owned(),
// US
vec![out, parse_statement_adv(file, true, 100)?],
)
.to()
}
(0..=100, Some('*')) => {
file.next();
SStatementEnum::FunctionCall(
"mul".to_owned(),
vec![out, parse_statement_adv(file, true, 100)?],
)
.to()
}
(0..=100, Some('/')) => {
file.next();
SStatementEnum::FunctionCall(
"div".to_owned(),
// RED SUSSY MOGUS MAN
vec![out, parse_statement_adv(file, true, 100)?],
)
.to()
}
(0..=100, Some('%')) => {
file.next();
SStatementEnum::FunctionCall(
"mod".to_owned(),
vec![out, parse_statement_adv(file, true, 100)?],
)
.to()
}
(0..=50, Some('>')) => {
file.next();
SStatementEnum::FunctionCall(
if let Some('=') = file.peek() {
file.next();
"gtoe".to_owned()
} else {
"gt".to_owned()
},
vec![out, parse_statement_adv(file, true, 50)?],
)
.to()
}
(0..=50, Some('<')) => {
file.next();
SStatementEnum::FunctionCall(
if let Some('=') = file.peek() {
file.next();
"ltoe".to_owned()
} else {
"lt".to_owned()
},
vec![out, parse_statement_adv(file, true, 50)?],
)
.to()
}
(0..=50, Some('='))
if matches!(
file.get_char(file.get_pos().current_char_index + 1),
Some('=')
) =>
{
file.next();
file.next();
SStatementEnum::FunctionCall(
"eq".to_owned(),
vec![out, parse_statement_adv(file, true, 50)?],
)
.to()
}
(0..=10, Some('=')) => {
file.next();
match out.statement.as_mut() {
SStatementEnum::Variable(name, r) => {
if name.starts_with("*") {
*name = name[1..].to_owned();
} else {
*r = true
}
}
_ => {}
}
parse_statement(file)?.output_to(out, 0)
}
_ => break,
}; };
err_end_of_prev = err_end_of_wrapper;
chain_length += 1;
}
}
} }
Ok(out) Ok(out)
} }

View File

@ -63,6 +63,7 @@ fn parse_mers_code(file: &mut File) -> Result<RScript, MacroError> {
} }
} }
#[derive(Debug)]
pub enum Macro { pub enum Macro {
/// Compiles and executes the provided mers code at compile-time and inserts the value /// Compiles and executes the provided mers code at compile-time and inserts the value
StaticMers(VData), StaticMers(VData),

View File

@ -2,6 +2,7 @@ use std::fmt::{self, Display, Formatter, Pointer};
use super::{code_macro::Macro, global_info::GlobalScriptInfo, val_data::VData, val_type::VType}; use super::{code_macro::Macro, global_info::GlobalScriptInfo, val_data::VData, val_type::VType};
#[derive(Debug)]
pub enum SStatementEnum { pub enum SStatementEnum {
Value(VData), Value(VData),
Tuple(Vec<SStatement>), Tuple(Vec<SStatement>),
@ -30,8 +31,9 @@ impl SStatementEnum {
} }
} }
#[derive(Debug)]
pub struct SStatement { pub struct SStatement {
pub output_to: Option<(String, usize)>, pub output_to: Option<(Box<SStatement>, usize)>,
pub statement: Box<SStatementEnum>, pub statement: Box<SStatementEnum>,
pub force_output_type: Option<VType>, pub force_output_type: Option<VType>,
} }
@ -44,8 +46,8 @@ impl SStatement {
force_output_type: None, force_output_type: None,
} }
} }
pub fn output_to(mut self, var: String, derefs: usize) -> Self { pub fn output_to(mut self, statement: SStatement, derefs: usize) -> Self {
self.output_to = Some((var, derefs)); self.output_to = Some((Box::new(statement), derefs));
self self
} }
// forces the statement's output to fit in a certain type. // forces the statement's output to fit in a certain type.
@ -56,6 +58,7 @@ impl SStatement {
} }
/// A block of code is a collection of statements. /// A block of code is a collection of statements.
#[derive(Debug)]
pub struct SBlock { pub struct SBlock {
pub statements: Vec<SStatement>, pub statements: Vec<SStatement>,
} }
@ -66,6 +69,7 @@ impl SBlock {
} }
// A function is a block of code that starts with some local variables as inputs and returns some value as its output. The last statement in the block will be the output. // A function is a block of code that starts with some local variables as inputs and returns some value as its output. The last statement in the block will be the output.
#[derive(Debug)]
pub struct SFunction { pub struct SFunction {
pub inputs: Vec<(String, VType)>, pub inputs: Vec<(String, VType)>,
pub block: SBlock, pub block: SBlock,

View File

@ -82,32 +82,23 @@ impl RFunction {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RStatement { pub struct RStatement {
// (_, _, is_init) // (_, _, is_init)
pub output_to: Option<(usize, usize, bool)>, pub output_to: Option<(Box<RStatement>, usize, bool)>,
statement: Box<RStatementEnum>, statement: Box<RStatementEnum>,
pub force_output_type: Option<VType>, pub force_output_type: Option<VType>,
} }
impl RStatement { impl RStatement {
pub fn run(&self, vars: &mut Vec<VData>, info: &GSInfo) -> VData { pub fn run(&self, vars: &mut Vec<VData>, info: &GSInfo) -> VData {
let out = self.statement.run(vars, info); let out = self.statement.run(vars, info);
if let Some((v, derefs, is_init)) = self.output_to { if let Some((v, derefs, is_init)) = &self.output_to {
// let mut parent = None; let mut val = v.run(vars, info);
let mut val = &mut vars[v]; // even if 0 derefs, deref once because it *has* to end on a reference (otherwise writing to it would be unacceptable as the value might not expect to be modified)
fn deref(n: usize, val: &mut VData, is_init: bool, out: VData) { for _ in 0..(derefs + 1) {
if n == 0 { val = match val.inner().deref() {
if is_init { Some(v) => v,
*val = out.inner().to(); None => unreachable!("can't dereference..."),
} else { };
}
val.assign(out.inner()); val.assign(out.inner());
}
} else {
if let VDataEnum::Reference(r) = &mut val.data().0 {
deref(n - 1, r, is_init, out)
} else {
unreachable!("cannot derefence: not a reference.");
}
}
}
deref(derefs, val, is_init, out);
VDataEnum::Tuple(vec![]).to() VDataEnum::Tuple(vec![]).to()
} else { } else {
out out
@ -370,6 +361,7 @@ impl RStatementEnum {
} }
} }
#[derive(Debug)]
pub struct RScript { pub struct RScript {
main: RFunction, main: RFunction,
info: GSInfo, info: GSInfo,

View File

@ -6,6 +6,7 @@ use super::{builtins, val_type::VType};
pub type GSInfo = Arc<GlobalScriptInfo>; pub type GSInfo = Arc<GlobalScriptInfo>;
#[derive(Debug)]
pub struct GlobalScriptInfo { pub struct GlobalScriptInfo {
pub vars: usize, pub vars: usize,

View File

@ -36,6 +36,7 @@ pub enum ToRunnableError {
found: VType, found: VType,
problematic: VType, problematic: VType,
}, },
CannotAssignTo(VType, VType),
CaseForceButTypeNotCovered(VType), CaseForceButTypeNotCovered(VType),
MatchConditionInvalidReturn(VType), MatchConditionInvalidReturn(VType),
NotIndexableFixed(VType, usize), NotIndexableFixed(VType, usize),
@ -129,6 +130,14 @@ impl ToRunnableError {
} }
write!(f, ".") write!(f, ".")
} }
Self::CannotAssignTo(val, target) => {
write!(f, "Cannot assign type ")?;
val.fmtgs(f, info)?;
write!(f, " to ")?;
target.fmtgs(f, info)?;
write!(f, ".")?;
Ok(())
},
Self::ForLoopContainerHasNoInnerTypes => { Self::ForLoopContainerHasNoInnerTypes => {
write!(f, "For loop: container had no inner types, cannot iterate.") write!(f, "For loop: container had no inner types, cannot iterate.")
} }
@ -332,7 +341,16 @@ fn statement(
ginfo: &mut GlobalScriptInfo, ginfo: &mut GlobalScriptInfo,
linfo: &mut LInfo, linfo: &mut LInfo,
) -> Result<RStatement, ToRunnableError> { ) -> Result<RStatement, ToRunnableError> {
let mut statement = match &*s.statement { statement_adv(s, ginfo, linfo, 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: Option<(VType, &mut bool)>,
) -> Result<RStatement, ToRunnableError> {
let mut state = match &*s.statement {
SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()), SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()),
SStatementEnum::Tuple(v) | SStatementEnum::List(v) => { SStatementEnum::Tuple(v) | SStatementEnum::List(v) => {
let mut w = Vec::with_capacity(v.len()); let mut w = Vec::with_capacity(v.len());
@ -346,6 +364,13 @@ fn statement(
} }
} }
SStatementEnum::Variable(v, is_ref) => { SStatementEnum::Variable(v, is_ref) => {
if !linfo.vars.contains_key(v) {
if let Some((t, is_init)) = to_be_assigned_to {
*is_init = true;
linfo.vars.insert(v.to_owned(), (ginfo.vars, t));
ginfo.vars += 1;
}
}
if let Some(var) = linfo.vars.get(v) { if let Some(var) = linfo.vars.get(v) {
RStatementEnum::Variable(var.0, { RStatementEnum::Variable(var.0, {
let mut v = var.1.clone(); stypes(&mut v, ginfo)?; v }, *is_ref) let mut v = var.1.clone(); stypes(&mut v, ginfo)?; v }, *is_ref)
@ -620,61 +645,103 @@ fn statement(
if let Some(force_opt) = &s.force_output_type { if let Some(force_opt) = &s.force_output_type {
let mut force_opt = force_opt.to_owned(); let mut force_opt = force_opt.to_owned();
stypes(&mut force_opt, ginfo)?; stypes(&mut force_opt, ginfo)?;
let real_output_type = statement.out(ginfo); let real_output_type = state.out(ginfo);
let problematic_types = real_output_type.fits_in(&force_opt, ginfo); let problematic_types = real_output_type.fits_in(&force_opt, ginfo);
if problematic_types.is_empty() { if problematic_types.is_empty() {
statement.force_output_type = Some(force_opt); state.force_output_type = Some(force_opt);
} else { } else {
return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types })); return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types }));
} }
} }
if let Some((opt, derefs)) = &s.output_to { if let Some((opt, derefs)) = &s.output_to {
if let Some((var_id, var_out)) = linfo.vars.get(opt) { let mut is_init = false;
let out = statement.out(ginfo); let optr = statement_adv(
let mut var_derefd = var_out.clone(); opt,
ginfo,
linfo,
if *derefs == 0 {
Some((state.out(ginfo), &mut is_init))
} else {
None
},
)?;
let mut opt_type = optr.out(ginfo);
for _ in 0..*derefs { for _ in 0..*derefs {
var_derefd = if let Some(v) = var_derefd.dereference() { if let Some(deref_type) = optr.out(ginfo).dereference() {
v opt_type = deref_type;
} else { } else {
return Err(ToRunnableError::CannotDereferenceTypeNTimes( return Err(ToRunnableError::CannotDereferenceTypeNTimes(
var_out.clone(), optr.out(ginfo),
*derefs, *derefs,
var_derefd, opt_type,
)); ));
} }
} }
let inv_types = out.fits_in(&var_derefd, ginfo); let opt_type_assign = match opt_type.dereference() {
if !inv_types.is_empty() { Some(v) => v,
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."); None => {
if *derefs != 0 {
return Err(ToRunnableError::CannotDeclareVariableWithDereference(
opt.clone(),
));
}
linfo.vars.insert(opt.clone(), (ginfo.vars, out));
statement.output_to = Some((ginfo.vars, 0, true));
ginfo.vars += 1;
} else {
// mutate existing variable
statement.output_to = Some((*var_id, *derefs, false));
}
} else {
let mut out = statement.out(ginfo);
for _ in 0..*derefs {
out = if let Some(v) = out.dereference() {
v
} else {
return Err(ToRunnableError::CannotDereferenceTypeNTimes( return Err(ToRunnableError::CannotDereferenceTypeNTimes(
statement.out(ginfo), optr.out(ginfo),
*derefs, derefs + 1,
out, opt_type,
))
}
};
if state.out(ginfo).fits_in(&opt_type_assign, ginfo).is_empty() {
state.output_to = Some((Box::new(optr), *derefs, is_init));
} else {
return Err(ToRunnableError::CannotAssignTo(
state.out(ginfo),
opt_type_assign,
)); ));
} }
//
// 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;
// }
} }
linfo.vars.insert(opt.clone(), (ginfo.vars, out)); Ok(state)
statement.output_to = Some((ginfo.vars, *derefs, true));
ginfo.vars += 1;
}
}
Ok(statement)
} }

View File

@ -24,8 +24,8 @@ impl VData {
return; return;
} }
} }
#[cfg(debug_assertions)] // #[cfg(debug_assertions)]
eprintln!("VData: assign: overwriting my previous Arc because it was immutable."); // eprintln!("VData: assign: overwriting my previous Arc because it was immutable.");
*self = new_val.to(); *self = new_val.to();
} }
pub fn inner_replace(&mut self, new_val: VDataEnum) -> VDataEnum { pub fn inner_replace(&mut self, new_val: VDataEnum) -> VDataEnum {
@ -90,22 +90,18 @@ impl VData {
} }
impl Clone for VData { impl Clone for VData {
fn clone(&self) -> Self { fn clone(&self) -> Self {
#[cfg(debug_assertions)]
eprint!("VData: Clone: ");
let mut d = self.data.lock().unwrap(); let mut d = self.data.lock().unwrap();
let o = if d.1 { let o = if d.1 {
if Arc::strong_count(&self.data) > 1 { if Arc::strong_count(&self.data) > 1 {
// mutable, copy the value to avoid accidentally modifying it. // mutable, copy the value to avoid accidentally modifying it.
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
eprint!( eprintln!(
"copying value due to clone of a mutable shared value. (strong count: {})", "VData: Clone: copying value due to clone of a mutable shared value. (strong count: {})",
Arc::strong_count(&self.data) Arc::strong_count(&self.data)
); );
d.0.clone().to() d.0.clone().to()
} else { } else {
// mutable, but not shared. just change it to not being mutable. // mutable, but not shared. just change it to not being mutable.
#[cfg(debug_assertions)]
eprint!("setting mutable value to immutable to avoid copying. clone will happen when value is used mutably.");
d.1 = false; d.1 = false;
// then return the same arc (-> avoid cloning) // then return the same arc (-> avoid cloning)
Self { Self {
@ -113,15 +109,11 @@ impl Clone for VData {
} }
} }
} else { } else {
#[cfg(debug_assertions)]
eprint!("immutably cloning immutable value. no copy necessary.");
// immutable, return the same arc (-> avoid cloning) // immutable, return the same arc (-> avoid cloning)
Self { Self {
data: Arc::clone(&self.data), data: Arc::clone(&self.data),
} }
}; };
#[cfg(debug_assertions)]
eprintln!();
o o
} }
} }
@ -229,6 +221,13 @@ impl VDataEnum {
data: Arc::new(Mutex::new((self, true))), data: Arc::new(Mutex::new((self, true))),
} }
} }
pub fn deref(self) -> Option<VData> {
if let Self::Reference(r) = self {
Some(r)
} else {
None
}
}
} }
// get() // get()

View File

@ -100,6 +100,7 @@ impl VType {
None None
} }
} }
/// returns Some(t) where t is the type you get from dereferencing self or None if self contains even a single type that cannot be dereferenced.
pub fn dereference(&self) -> Option<Self> { pub fn dereference(&self) -> Option<Self> {
let mut out = Self::empty(); let mut out = Self::empty();
for t in self.types.iter() { for t in self.types.iter() {

11
mers/t.mers Normal file
View File

@ -0,0 +1,11 @@
a = (max int) {
println("Max: " + max.to_string())
for i max {
println(i.to_string())
}
}
a.debug()
// why does this work
a.thread(10).await()
// and this just blocks and does nothing
a.run(10)

View File

@ -11,10 +11,12 @@ progress = 0
calculator = (max int) { calculator = (max int) {
sum = 0 sum = 0
for i max { for i max {
i = i.add(1) i = i + 1
sum = sum.add(i) // println("i: {0}".format(i.to_string()))
println("i: {0} s: {1}".format(i.to_string() sum.to_string()))
sum = sum + i + 1
if fake_delay sleep(1) if fake_delay sleep(1)
progress = i.mul(100).div(max) v = i * 100 / max
} }
"the sum of all numbers from 0 to {0} is {1}!".format(max.to_string() sum.to_string()) "the sum of all numbers from 0 to {0} is {1}!".format(max.to_string() sum.to_string())
} }
@ -26,7 +28,7 @@ slow_calculation_thread = calculator.thread(if fake_delay 10 else 20000000)
loop { loop {
sleep(1) sleep(1)
println("{0}%".format(progress.to_string())) println("{0}%".format(progress.to_string()))
progress.eq(100) // break from the loop 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. // use await() to get the result from the thread. if the thread is still running, this will block until the thread finishes.

8
when_clone.mers Normal file
View File

@ -0,0 +1,8 @@
&a = "value"
&list = ["a" "b" "c" ...]
&elem = &list.get_ref(1)
switch! elem {
[&string] elem.0 = "z"
[] {}
}
list.debug()