added arrow syntax for forcing output types and added the "any" type (which is basically useless and will likely change in the future)

This commit is contained in:
mark 2023-06-04 21:33:35 +02:00
parent efe8a177dc
commit db0be51fa4
11 changed files with 142 additions and 54 deletions

View File

@ -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"`, ...

View File

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

View File

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

View File

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

View File

@ -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<Macro, MacroError> {
file.skip_whitespaces();
@ -63,12 +71,6 @@ fn parse_mers_code(file: &mut File) -> Result<RScript, MacroError> {
}
}
#[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 {

View File

@ -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 }
}
}
@ -270,6 +270,7 @@ impl FormatGs for SStatement {
if let Some(force_opt) = &self.force_output_type {
write!(f, "-> ")?;
force_opt.fmtgs(f, info, form, file)?;
write!(f, " ")?;
}
write!(f, "{}", "*".repeat(self.derefs))?;
self.statement.fmtgs(f, info, form, file)?;
@ -300,7 +301,7 @@ impl FormatGs for SFunction {
t.fmtgs(f, info, form, file)?;
}
write!(f, ") ")?;
self.block.fmtgs(f, info, form, file)
self.statement.fmtgs(f, info, form, file)
}
}

View File

@ -63,13 +63,13 @@ pub struct RFunction {
pub inputs: Vec<Arc<Mutex<VData>>>,
pub input_types: Vec<VType>,
pub input_output_map: Vec<(Vec<VSingleType>, 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<VSingleType>) -> VType {
pub fn out(&self, input_types: &Vec<VSingleType>, 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<VType>, info: &GlobalScriptInfo) -> VType {
let mut out = VType { types: vec![] };
@ -92,10 +92,16 @@ impl RFunction {
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)
} else {
out
}
}
pub fn out_all(&self, info: &GlobalScriptInfo) -> VType {
self.block.out(info)
self.statement.out(info)
}
pub fn in_types(&self) -> &Vec<VType> {
&self.input_types

View File

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

View File

@ -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<VType> {
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<VType> {
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<VType> {
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<VType> {
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<VType> {
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<VType> {
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<String, usize>) {
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 {

View File

@ -390,6 +390,7 @@ impl ByteData for VType {
impl ByteDataA for VSingleType {
fn as_byte_data(&self, vec: &mut Vec<u8>) {
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,

View File

@ -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<SStatement, ParseError> {
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<VType, ParseError> {
@ -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,