mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
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:
parent
6f31abd5cc
commit
bba48b311f
19
amogus.mers
Normal file
19
amogus.mers
Normal file
File diff suppressed because one or more lines are too long
10
get_ref.mers
10
get_ref.mers
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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
4
mers/a.mers
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
a = 1
|
||||||
|
b = 2
|
||||||
|
a.debug()
|
||||||
|
b.debug()
|
@ -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());
|
||||||
|
@ -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,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
@ -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();
|
||||||
file.skip_whitespaces();
|
// special characters that can follow a statement (loop because these can be chained)
|
||||||
if !file[file.get_pos().current_char_index..].starts_with("..") {
|
loop {
|
||||||
// dot chain syntax only works if there is only one dot
|
file.skip_whitespaces();
|
||||||
if let Some('.') = file.peek() {
|
out = match (chain_level, file.peek()) {
|
||||||
// consume the dot (otherwise, a.b.c syntax will break in certain cases)
|
(0..=200, Some('.'))
|
||||||
file.next();
|
if !matches!(
|
||||||
}
|
file.get_char(file.get_pos().current_char_index + 1),
|
||||||
if !is_part_of_chain_already {
|
Some('.')
|
||||||
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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
err_end_of_prev = err_end_of_wrapper;
|
|
||||||
chain_length += 1;
|
|
||||||
}
|
}
|
||||||
}
|
(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,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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,
|
||||||
|
@ -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());
|
|
||||||
}
|
|
||||||
} 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);
|
val.assign(out.inner());
|
||||||
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,
|
||||||
|
@ -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,
|
||||||
|
|
||||||
|
@ -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,11 +364,18 @@ 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)
|
||||||
} else {
|
} else {
|
||||||
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
|
return Err(ToRunnableError::UseOfUndefinedVariable(v.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SStatementEnum::FunctionCall(v, args) => {
|
SStatementEnum::FunctionCall(v, args) => {
|
||||||
@ -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,
|
||||||
for _ in 0..*derefs {
|
ginfo,
|
||||||
var_derefd = if let Some(v) = var_derefd.dereference() {
|
linfo,
|
||||||
v
|
if *derefs == 0 {
|
||||||
} else {
|
Some((state.out(ginfo), &mut is_init))
|
||||||
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));
|
|
||||||
statement.output_to = Some((ginfo.vars, 0, true));
|
|
||||||
ginfo.vars += 1;
|
|
||||||
} else {
|
} else {
|
||||||
// mutate existing variable
|
None
|
||||||
statement.output_to = Some((*var_id, *derefs, false));
|
},
|
||||||
|
)?;
|
||||||
|
let mut opt_type = optr.out(ginfo);
|
||||||
|
for _ in 0..*derefs {
|
||||||
|
if let Some(deref_type) = optr.out(ginfo).dereference() {
|
||||||
|
opt_type = deref_type;
|
||||||
|
} else {
|
||||||
|
return Err(ToRunnableError::CannotDereferenceTypeNTimes(
|
||||||
|
optr.out(ginfo),
|
||||||
|
*derefs,
|
||||||
|
opt_type,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let mut out = statement.out(ginfo);
|
|
||||||
for _ in 0..*derefs {
|
|
||||||
out = if let Some(v) = out.dereference() {
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
return Err(ToRunnableError::CannotDereferenceTypeNTimes(
|
|
||||||
statement.out(ginfo),
|
|
||||||
*derefs,
|
|
||||||
out,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
linfo.vars.insert(opt.clone(), (ginfo.vars, out));
|
|
||||||
statement.output_to = Some((ginfo.vars, *derefs, true));
|
|
||||||
ginfo.vars += 1;
|
|
||||||
}
|
}
|
||||||
|
let opt_type_assign = match opt_type.dereference() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
return Err(ToRunnableError::CannotDereferenceTypeNTimes(
|
||||||
|
optr.out(ginfo),
|
||||||
|
derefs + 1,
|
||||||
|
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;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
Ok(statement)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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
11
mers/t.mers
Normal 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)
|
10
thread.mers
10
thread.mers
@ -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
8
when_clone.mers
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
&a = "value"
|
||||||
|
&list = ["a" "b" "c" ...]
|
||||||
|
&elem = &list.get_ref(1)
|
||||||
|
switch! elem {
|
||||||
|
[&string] elem.0 = "z"
|
||||||
|
[] {}
|
||||||
|
}
|
||||||
|
list.debug()
|
Loading…
Reference in New Issue
Block a user