From db0be51fa424cff1978552e97c94468b60df850e Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 4 Jun 2023 21:33:35 +0200 Subject: [PATCH] added arrow syntax for forcing output types and added the "any" type (which is basically useless and will likely change in the future) --- docs/statements.md | 17 ++++++++++++- examples/force_output_type.mers | 3 +++ examples/the_any_type.mers | 13 ++++++++++ mers/src/lang/builtins.rs | 2 +- mers/src/lang/code_macro.rs | 16 ++++++------ mers/src/lang/code_parsed.rs | 13 +++++----- mers/src/lang/code_runnable.rs | 18 +++++++++----- mers/src/lang/to_runnable.rs | 44 +++++++++++++++++++-------------- mers/src/lang/val_type.rs | 34 +++++++++++++++++++------ mers/src/libs/comms.rs | 2 ++ mers/src/parsing/parse.rs | 34 +++++++++++++++++++------ 11 files changed, 142 insertions(+), 54 deletions(-) create mode 100644 examples/force_output_type.mers create mode 100644 examples/the_any_type.mers diff --git a/docs/statements.md b/docs/statements.md index f991732..aa582e7 100644 --- a/docs/statements.md +++ b/docs/statements.md @@ -1,8 +1,23 @@ -# mers statements overview +# Mers statements overview This is the documentation for mers statements. In code, statements are represented by `SStatement`, `SStatementEnum`, `RStatement`, and `RStatementEnum`. +## statement prefixes + +A statement can be prefixed with any number of stars `*`. This is called dereferencing and turns a reference to a value into the value itself. Modifying the value after a dereference leaves the value the reference was pointing to untouched (the data will be cloned to achieve this). + +A statement can also be prefixed with an arrow followed by the type the statement will have: `-> int/float 12`. +Although the statement will always return an `int`, mers will assume that it could also return a float. +If the statement's output doesn't fit in the forced type (`-> int "text"`), mers will generate an error. +In combination with functions, this is similar to rust's syntax: + + fn my_func(a int, b int) -> int { + a + b + } + +# Different statements + ## Value A statement that always returns a certain value: `10`, `"Hello, World"`, ... diff --git a/examples/force_output_type.mers b/examples/force_output_type.mers new file mode 100644 index 0000000..a6c28e5 --- /dev/null +++ b/examples/force_output_type.mers @@ -0,0 +1,3 @@ +fn plus(a int, b int) -> int { a + b } + +10.plus(20).debug() diff --git a/examples/the_any_type.mers b/examples/the_any_type.mers new file mode 100644 index 0000000..61be624 --- /dev/null +++ b/examples/the_any_type.mers @@ -0,0 +1,13 @@ +fn debug_any(val any) { + val.debug() +} + +"something".debug_any() + +fn just_return(val any) { + val +}.debug() + +v := "text" +v.debug() +v.just_return().debug() diff --git a/mers/src/lang/builtins.rs b/mers/src/lang/builtins.rs index 5892018..8f4f944 100755 --- a/mers/src/lang/builtins.rs +++ b/mers/src/lang/builtins.rs @@ -979,7 +979,7 @@ impl BuiltinFunction { run_input_types.push(val.out_single()); *var.lock().unwrap() = val; } - let out_type = f.out(&run_input_types); + let out_type = f.out(&run_input_types, &info); let info = Arc::clone(info); let f = Arc::clone(f); VDataEnum::Thread( diff --git a/mers/src/lang/code_macro.rs b/mers/src/lang/code_macro.rs index 3c940f3..3696d0f 100755 --- a/mers/src/lang/code_macro.rs +++ b/mers/src/lang/code_macro.rs @@ -5,7 +5,15 @@ use crate::parsing::{ parse::{self, ParseError, ScriptError}, }; -use super::{code_runnable::RScript, val_data::VData}; +use super::{code_parsed::SStatement, code_runnable::RScript, val_data::VData, val_type::VType}; + +// macro format is !(macro_type [...]) + +#[derive(Debug)] +pub enum Macro { + /// Compiles and executes the provided mers code at compile-time and inserts the value + StaticMers(VData), +} pub fn parse_macro(file: &mut File) -> Result { file.skip_whitespaces(); @@ -63,12 +71,6 @@ fn parse_mers_code(file: &mut File) -> Result { } } -#[derive(Debug)] -pub enum Macro { - /// Compiles and executes the provided mers code at compile-time and inserts the value - StaticMers(VData), -} - impl Display for Macro { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/mers/src/lang/code_parsed.rs b/mers/src/lang/code_parsed.rs index c662f62..b437847 100755 --- a/mers/src/lang/code_parsed.rs +++ b/mers/src/lang/code_parsed.rs @@ -83,11 +83,11 @@ impl SBlock { #[derive(Debug)] pub struct SFunction { pub inputs: Vec<(String, VType)>, - pub block: SBlock, + pub statement: SStatement, } impl SFunction { - pub fn new(inputs: Vec<(String, VType)>, block: SBlock) -> Self { - Self { inputs, block } + pub fn new(inputs: Vec<(String, VType)>, statement: SStatement) -> Self { + Self { inputs, statement } } } @@ -268,8 +268,9 @@ impl FormatGs for SStatement { } // output self if let Some(force_opt) = &self.force_output_type { - write!(f, " -> ")?; + write!(f, "-> ")?; force_opt.fmtgs(f, info, form, file)?; + write!(f, " ")?; } write!(f, "{}", "*".repeat(self.derefs))?; self.statement.fmtgs(f, info, form, file)?; @@ -293,14 +294,14 @@ impl FormatGs for SFunction { write!(f, "(")?; for (i, (name, t)) in self.inputs.iter().enumerate() { if i > 0 { - write!(f, " {name}")?; + write!(f, " {name} ")?; } else { write!(f, "{name} ")?; } t.fmtgs(f, info, form, file)?; } write!(f, ") ")?; - self.block.fmtgs(f, info, form, file) + self.statement.fmtgs(f, info, form, file) } } diff --git a/mers/src/lang/code_runnable.rs b/mers/src/lang/code_runnable.rs index 0436779..babfbd6 100755 --- a/mers/src/lang/code_runnable.rs +++ b/mers/src/lang/code_runnable.rs @@ -63,13 +63,13 @@ pub struct RFunction { pub inputs: Vec>>, pub input_types: Vec, pub input_output_map: Vec<(Vec, VType)>, - pub block: RBlock, + pub statement: RStatement, } impl RFunction { pub fn run(&self, info: &GSInfo) -> VData { - self.block.run(info) + self.statement.run(info) } - pub fn out(&self, input_types: &Vec) -> VType { + pub fn out(&self, input_types: &Vec, info: &GlobalScriptInfo) -> VType { self.input_output_map .iter() .find_map(|v| { @@ -79,7 +79,7 @@ impl RFunction { None } }) - .expect("invalid args for function! possible issue with type-checker if this can be reached! feel free to report a bug.") + .unwrap_or_else(|| self.statement.out(info)) } pub fn out_vt(&self, input_types: &Vec, info: &GlobalScriptInfo) -> VType { let mut out = VType { types: vec![] }; @@ -92,10 +92,16 @@ impl RFunction { out = out | otype; } } - out + 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) + } else { + out + } } pub fn out_all(&self, info: &GlobalScriptInfo) -> VType { - self.block.out(info) + self.statement.out(info) } pub fn in_types(&self) -> &Vec { &self.input_types diff --git a/mers/src/lang/to_runnable.rs b/mers/src/lang/to_runnable.rs index 921fd82..cf4063e 100755 --- a/mers/src/lang/to_runnable.rs +++ b/mers/src/lang/to_runnable.rs @@ -263,12 +263,12 @@ fn get_all_functions( vartype.to() } } - // block is parsed multiple times (this is why we get duplicates in stderr): + // 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(), - block(&s.block, ginfo, linfo.clone())?.out(ginfo), + statement(&s.statement, ginfo, &mut linfo.clone())?.out(ginfo), )); Ok(()) } @@ -307,7 +307,7 @@ fn function( inputs: input_vars, input_types, input_output_map: all_outs, - block: block(&s.block, ginfo, linfo)?, + statement: statement(&s.statement, ginfo, &mut linfo.clone())?, }) } @@ -331,7 +331,11 @@ pub fn stypes(t: &mut VType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunna } pub fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> { match t { - VSingleType::Bool | VSingleType::Int | VSingleType::Float | VSingleType::String => (), + VSingleType::Any + | VSingleType::Bool + | VSingleType::Int + | VSingleType::Float + | VSingleType::String => (), VSingleType::Tuple(v) => { for t in v { stypes(t, ginfo)?; @@ -398,7 +402,7 @@ fn statement_adv( // eprintln!(" --> {}", t.0); // } let mut state = match &*s.statement { - SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()), + SStatementEnum::Value(v) => RStatementEnum::Value(v.clone()).to(), SStatementEnum::Tuple(v) | SStatementEnum::List(v) => { let mut w = Vec::with_capacity(v.len()); let mut prev = None; @@ -425,6 +429,7 @@ fn statement_adv( } else { RStatementEnum::Tuple(w) } + .to() } SStatementEnum::Variable(v, is_ref) => { let existing_var = linfo.vars.get(v); @@ -465,6 +470,7 @@ fn statement_adv( } else { return Err(ToRunnableError::UseOfUndefinedVariable(v.clone())); } + .to() } SStatementEnum::FunctionCall(v, args) => { let mut rargs = Vec::with_capacity(args.len()); @@ -553,6 +559,7 @@ fn statement_adv( } } } + .to(), SStatementEnum::FunctionDefinition(name, f) => { let f = Arc::new(function(f, ginfo, linfo.clone())?); if let Some(name) = name { @@ -564,9 +571,9 @@ fn statement_adv( linfo.fns.insert(name.clone(), vec![f]); } } - RStatementEnum::Value(VDataEnum::Function(f).to()) + RStatementEnum::Value(VDataEnum::Function(f).to()).to() } - SStatementEnum::Block(b) => RStatementEnum::Block(block(&b, ginfo, linfo.clone())?), + SStatementEnum::Block(b) => RStatementEnum::Block(block(&b, ginfo, linfo.clone())?).to(), SStatementEnum::If(c, t, e) => RStatementEnum::If( { let condition = statement(&c, ginfo, linfo)?; @@ -591,8 +598,9 @@ fn statement_adv( Some(v) => Some(statement(&v, ginfo, linfo)?), None => None, }, - ), - SStatementEnum::Loop(c) => RStatementEnum::Loop(statement(&c, ginfo, linfo)?), + ) + .to(), + SStatementEnum::Loop(c) => RStatementEnum::Loop(statement(&c, ginfo, linfo)?).to(), SStatementEnum::For(v, c, b) => { let mut linfo = linfo.clone(); let container = statement(&c, ginfo, &mut linfo)?; @@ -603,7 +611,7 @@ fn statement_adv( let assign_to = statement_adv(v, ginfo, &mut linfo, &mut Some((inner, &mut true)))?; let block = statement(&b, ginfo, &mut linfo)?; let o = RStatementEnum::For(assign_to, container, block); - o + o.to() } SStatementEnum::Switch(switch_on, cases, force) => { @@ -639,7 +647,7 @@ fn statement_adv( })); } } - RStatementEnum::Switch(switch_on, ncases, *force) + RStatementEnum::Switch(switch_on, ncases, *force).to() } SStatementEnum::Match(cases) => { let mut ncases: Vec<(RStatement, RStatement, RStatement)> = @@ -667,13 +675,13 @@ fn statement_adv( out_type = out_type | VSingleType::Tuple(vec![]).to(); } - RStatementEnum::Match(ncases) + RStatementEnum::Match(ncases).to() } SStatementEnum::IndexFixed(st, i) => { let st = statement(st, ginfo, linfo)?; if st.out(ginfo).get_always(*i, ginfo).is_some() { - RStatementEnum::IndexFixed(st, *i) + RStatementEnum::IndexFixed(st, *i).to() } else { return Err(ToRunnableError::NotIndexableFixed(st.out(ginfo), *i)); } @@ -689,7 +697,8 @@ fn statement_adv( } }, statement(s, ginfo, linfo)?, - ), + ) + .to(), SStatementEnum::TypeDefinition(name, t) => { // insert to name map has to happen before stypes() ginfo @@ -698,13 +707,12 @@ fn statement_adv( let mut t = t.to_owned(); stypes(&mut t, ginfo)?; ginfo.custom_types.push(t); - RStatementEnum::Value(VDataEnum::Tuple(vec![]).to()) + RStatementEnum::Value(VDataEnum::Tuple(vec![]).to()).to() } SStatementEnum::Macro(m) => match m { - Macro::StaticMers(val) => RStatementEnum::Value(val.clone()), + Macro::StaticMers(val) => RStatementEnum::Value(val.clone()).to(), }, - } - .to(); + }; state.derefs = s.derefs; // if force_output_type is set, verify that the real output type actually fits in the forced one. if let Some(force_opt) = &s.force_output_type { diff --git a/mers/src/lang/val_type.rs b/mers/src/lang/val_type.rs index c1020ab..4374279 100755 --- a/mers/src/lang/val_type.rs +++ b/mers/src/lang/val_type.rs @@ -18,6 +18,7 @@ pub struct VType { #[derive(Clone, Debug, PartialEq, Eq)] pub enum VSingleType { + Any, Bool, Int, Float, @@ -37,7 +38,8 @@ impl VSingleType { /// None => Cannot get, Some(t) => getting can return t or nothing pub fn get(&self, i: usize, gsinfo: &GlobalScriptInfo) -> Option { match self { - Self::Bool + Self::Any + | Self::Bool | Self::Int | Self::Float | Self::Function(..) @@ -56,7 +58,8 @@ impl VSingleType { } pub fn get_ref(&self, i: usize, gsinfo: &GlobalScriptInfo) -> Option { match self { - Self::Bool + Self::Any + | Self::Bool | Self::Int | Self::Float | Self::Function(..) @@ -76,7 +79,8 @@ impl VSingleType { /// None => might not always return t, Some(t) => can only return t pub fn get_always(&self, i: usize, info: &GlobalScriptInfo) -> Option { match self { - Self::Bool + Self::Any + | Self::Bool | Self::Int | Self::Float | Self::String @@ -95,7 +99,8 @@ impl VSingleType { } pub fn get_always_ref(&self, i: usize, info: &GlobalScriptInfo) -> Option { match self { - Self::Bool + Self::Any + | Self::Bool | Self::Int | Self::Float | Self::String @@ -179,7 +184,12 @@ impl VType { impl VSingleType { pub fn get_any(&self, info: &GlobalScriptInfo) -> Option { match self { - Self::Bool | Self::Int | Self::Float | Self::Function(..) | Self::Thread(..) => None, + Self::Any + | Self::Bool + | Self::Int + | Self::Float + | Self::Function(..) + | Self::Thread(..) => None, Self::String => Some(VSingleType::String.into()), Self::Tuple(t) => Some(t.iter().fold(VType { types: vec![] }, |a, b| a | b)), Self::List(t) => Some(t.clone()), @@ -192,7 +202,12 @@ impl VSingleType { } pub fn get_any_ref(&self, info: &GlobalScriptInfo) -> Option { match self { - Self::Bool | Self::Int | Self::Float | Self::Function(..) | Self::Thread(..) => None, + Self::Any + | Self::Bool + | Self::Int + | Self::Float + | Self::Function(..) + | Self::Thread(..) => None, Self::String => Some(VSingleType::String.into()), Self::Tuple(t) => Some( t.iter() @@ -378,7 +393,7 @@ impl VSingleType { /// converts all Self::EnumVariantS to Self::EnumVariant pub fn enum_variants(&mut self, enum_variants: &mut HashMap) { match self { - Self::Bool | Self::Int | Self::Float | Self::String => (), + Self::Any | Self::Bool | Self::Int | Self::Float | Self::String => (), Self::Tuple(v) => { for t in v { t.enum_variants(enum_variants); @@ -412,6 +427,8 @@ impl VSingleType { } pub fn fits_in(&self, rhs: &Self, info: &GlobalScriptInfo) -> bool { let o = match (self, rhs) { + (_, Self::Any) => true, + (Self::Any, _) => false, (Self::Reference(r), Self::Reference(b)) => r.fits_in(b, info), (Self::Reference(_), _) | (_, Self::Reference(_)) => false, (Self::EnumVariant(v1, t1), Self::EnumVariant(v2, t2)) => { @@ -526,6 +543,7 @@ impl FormatGs for VSingleType { file: Option<&crate::parsing::file::File>, ) -> std::fmt::Result { match self { + Self::Any => write!(f, "any"), Self::Bool => write!(f, "bool"), Self::Int => write!(f, "int"), Self::Float => write!(f, "float"), @@ -649,7 +667,7 @@ impl FormatGs for VType { } t.fmtgs(f, info, form, file)?; } - Ok(()) + write!(f, ",") } } impl Display for VType { diff --git a/mers/src/libs/comms.rs b/mers/src/libs/comms.rs index edc1554..4e9856c 100644 --- a/mers/src/libs/comms.rs +++ b/mers/src/libs/comms.rs @@ -390,6 +390,7 @@ impl ByteData for VType { impl ByteDataA for VSingleType { fn as_byte_data(&self, vec: &mut Vec) { match self { + Self::Any => vec.push(b'a'), Self::Bool => vec.push(b'b'), Self::Int => vec.push(b'i'), Self::Float => vec.push(b'f'), @@ -438,6 +439,7 @@ impl ByteData for VSingleType { let mut switch_byte = [0u8]; data.read_exact(&mut switch_byte)?; Ok(match switch_byte[0] { + b'a' => Self::Any, b'b' => Self::Bool, b'i' => Self::Int, b'f' => Self::Float, diff --git a/mers/src/parsing/parse.rs b/mers/src/parsing/parse.rs index a38d1d6..6878eb1 100755 --- a/mers/src/parsing/parse.rs +++ b/mers/src/parsing/parse.rs @@ -192,7 +192,7 @@ pub fn parse_step_interpret( "args".to_string(), VSingleType::List(VSingleType::String.into()).to(), )], - parse_block_advanced(file, Some(false), true, true, false)?, + SStatementEnum::Block(parse_block_advanced(file, Some(false), false, true, false)?).to(), ); if ginfo.log.after_parse.log() { ginfo.log.log(LogMsg::AfterParse( @@ -543,6 +543,18 @@ pub mod implementation { ) -> Result { file.skip_whitespaces(); let err_start_of_statement = *file.get_pos(); + // force output type + let force_opt = if file[file.get_pos().current_char_index..].starts_with("->") { + file.next(); + file.next(); + file.skip_whitespaces(); + let o = parse_type(file)?; + file.skip_whitespaces(); + Some(o) + } else { + None + }; + // derefs let mut derefs = 0; loop { if let Some('*') = file.peek() { @@ -836,6 +848,7 @@ pub mod implementation { } }; out.derefs = derefs; + out.force_output_type = force_opt; let err_end_of_original_statement = *file.get_pos(); // special characters that can follow a statement (loop because these can be chained) loop { @@ -1070,6 +1083,7 @@ pub mod implementation { } else { loop { let mut arg_name = String::new(); + file.skip_whitespaces(); loop { let err_fn_arg_name_start = *file.get_pos(); match file.next() { @@ -1100,7 +1114,7 @@ pub mod implementation { } } } - Ok(SFunction::new(args, parse_block(file)?)) + Ok(SFunction::new(args, parse_statement(file)?)) } pub(crate) fn parse_type(file: &mut File) -> Result { @@ -1122,7 +1136,6 @@ pub mod implementation { closed_fn_args = true; break; } - file.skip_whitespaces(); match file.peek() { Some('/') => { file.next(); @@ -1132,8 +1145,15 @@ pub mod implementation { file.next(); break; } - Some(_) => break, - + Some(ch) if ch.is_whitespace() => break, + Some(ch) if ch == ',' => { + file.next(); + break; + } + Some(ch) => { + eprintln!("[warn] stopped parsing type at character {ch} (unexpected character, moving on...)"); + break; + } None => break, } } @@ -1201,8 +1221,7 @@ pub mod implementation { let mut name = ch.to_string(); loop { match file.peek() { - Some(']') => break, - Some('/') => break, + Some(']' | '/' | ',') => break, Some(')') if in_fn_args => { file.next(); closed_bracket_in_fn_args = true; @@ -1315,6 +1334,7 @@ pub mod implementation { } } match name.trim().to_lowercase().as_str() { + "any" => VSingleType::Any, "bool" => VSingleType::Bool, "int" => VSingleType::Int, "float" => VSingleType::Float,