From c7642ef9111fe9b9efd53697491fcf6abf14efcd Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 4 Jun 2023 20:07:50 +0200 Subject: [PATCH] there can now be multiple functions with the same name. The most recently declared one will be preferred if multiple would be valid. --- docs/statements.md | 130 +++++++++++++++++++++ examples/functions_double_definitions.mers | 27 +++++ mers/src/lang/code_runnable.rs | 12 +- mers/src/lang/to_runnable.rs | 65 ++++++----- 4 files changed, 201 insertions(+), 33 deletions(-) create mode 100644 docs/statements.md create mode 100644 examples/functions_double_definitions.mers diff --git a/docs/statements.md b/docs/statements.md new file mode 100644 index 0000000..f991732 --- /dev/null +++ b/docs/statements.md @@ -0,0 +1,130 @@ +# mers statements overview + +This is the documentation for mers statements. +In code, statements are represented by `SStatement`, `SStatementEnum`, `RStatement`, and `RStatementEnum`. + +## Value + +A statement that always returns a certain value: `10`, `"Hello, World"`, ... + +## Code block + +Code blocks are a single statement, but they can contain any number of statements. +They have their own scope, so local variables declared in them aren't accessible from outside. + +They return whatever the last statement they contain returns. Empty code blocks (ones without any statements: `{}`) return `[]`. + + list := [1, 2, 3, 4 ...] + sum := { + // a temporary variable that only exists in this block + counter := 0 + for elem list { + &counter = counter + elem + } + // the final value will be returned from the block and then assigned to sum. + counter + } + +## Tuple and list + +These statements contain any number of statements and return the returned values in order as a tuple or list. + + // x is a tuple and has type [int string float] + x := [ + get_num(), + get_text(), + 12.5 + ] + // y is a list and has type [int/string/float ...] + x := [ + get_num(), + get_text(), + 12.5 + ...] + +## Variable + +These statements retrieve the value from a variable name: + + x := "my value" + println( + x // <- this is a variable statement - it gets the "my value" from above so the println function can use it. + ) + +They can also get references if the variable name is prefixed with the `&` symbol: + + debug(&x) // type is &string + +This is why `&` is required to assign to a variable: + + x = "something else" // can't assign to string! + &x = "something else" // only data behind references can be changed - we can assign to &string. + +## Function call + +Function calls give some data to a function and return the result from that function. + +There are 3 kinds of function calls: + +- calls to builtin functions +- calls to library functions +- calls to custom functions + +Anonymous functions are called using the builtin `run` function. + +## If statement + +Allow for conditional execution of code paths: + + if condition() { + // do something + } + + if condition() { + // do something + } else { + // do something else + } + +## Loop + +Executes code repeatedly: + + counter := 0 + loop { + &counter = counter + 1 + println("c: " + counter.to_string()) + } + +## For loop + +## Switch + +## Match + +## Enum + +Wrap a value in an enum: `EnumName: ` + +# dot-chains + +Statements can be followed by the dot `.` character. This has different meanings depending on what comes after the dot: + +## A function call + +In this case, the statement before the dot is prepended to the function arguments: + + a.func(b, c) + func(a, b, c) + +## An integer + +Gets the n-th thing from a tuple (uses zero-based indexing): + + [1, "2", 3.0].1 == "2" + +If the value before the dot is a reference to a tuple, a reference to the thing inside the tuple will be obtained: + + x := [1, "2", 3.0] + // we assign to the "2", which changes it. + &x.1 = "4" diff --git a/examples/functions_double_definitions.mers b/examples/functions_double_definitions.mers new file mode 100644 index 0000000..2f394c3 --- /dev/null +++ b/examples/functions_double_definitions.mers @@ -0,0 +1,27 @@ +type person [string int] +fn name(p person/&person) p.0 +fn age(p person/&person) p.1 + +type village [[float float] string] +fn name(v village/&village) v.1 +fn location(v village/&village) v.0 +fn x_coordinate(v village/&village) v.0.0 +fn y_coordinate(v village/&village) v.0.1 + + + +customer := ["Max M.", 43] +home_town := [[12.3, 5.09], "Maxburg"] + +debug(customer) +debug(customer.name()) +debug(&customer.name()) + +debug(home_town) +debug(home_town.name()) +debug(home_town.location()) + +println( + "Hello, " + customer.name() + + " from " + home_town.name() +) diff --git a/mers/src/lang/code_runnable.rs b/mers/src/lang/code_runnable.rs index c5c8492..226048d 100755 --- a/mers/src/lang/code_runnable.rs +++ b/mers/src/lang/code_runnable.rs @@ -19,8 +19,8 @@ pub enum RStatementEnum { List(Vec), Variable(Arc>, VType, bool), FunctionCall(Arc, Vec), - BuiltinFunction(BuiltinFunction, Vec), - LibFunction(usize, usize, Vec, VType), + BuiltinFunctionCall(BuiltinFunction, Vec), + LibFunctionCall(usize, usize, Vec, VType), Block(RBlock), If(RStatement, RStatement, Option), Loop(RStatement), @@ -186,8 +186,8 @@ impl RStatementEnum { } func.run(info) } - Self::BuiltinFunction(v, args) => v.run(args, info), - Self::LibFunction(libid, fnid, args, _) => { + Self::BuiltinFunctionCall(v, args) => v.run(args, info), + Self::LibFunctionCall(libid, fnid, args, _) => { info.libs[*libid].run_fn(*fnid, args.iter().map(|arg| arg.run(info)).collect()) } Self::Block(b) => b.run(info), @@ -343,7 +343,7 @@ impl RStatementEnum { Self::FunctionCall(f, args) => { f.out_vt(&args.iter().map(|v| v.out(info)).collect(), info) } - Self::LibFunction(.., out) => out.clone(), + Self::LibFunctionCall(.., out) => out.clone(), Self::Block(b) => b.out(info), Self::If(_, a, b) => { if let Some(b) = b { @@ -354,7 +354,7 @@ impl RStatementEnum { } Self::Loop(c) => c.out(info).matches().1, Self::For(_, _, b) => VSingleType::Tuple(vec![]).to() | b.out(info).matches().1, - Self::BuiltinFunction(f, args) => { + Self::BuiltinFunctionCall(f, args) => { f.returns(args.iter().map(|rs| rs.out(info)).collect(), info) } Self::Switch(switch_on, cases, force) => { diff --git a/mers/src/lang/to_runnable.rs b/mers/src/lang/to_runnable.rs index e8faa5f..0f3f7a6 100755 --- a/mers/src/lang/to_runnable.rs +++ b/mers/src/lang/to_runnable.rs @@ -33,7 +33,7 @@ pub enum ToRunnableError { CannotDeclareVariableWithDereference(String), CannotDereferenceTypeNTimes(VType, usize, VType), FunctionWrongArgCount(String, usize, usize), - FunctionWrongArgs(Vec, String), + FunctionWrongArgs(String, Vec>, Vec), InvalidType { expected: VType, found: VType, @@ -87,7 +87,7 @@ impl FormatGs for ToRunnableError { Ok(()) }, Self::FunctionWrongArgCount(v, a, b) => write!(f, "Tried to call function \"{v}\", which takes {a} arguments, with {b} arguments instead."), - Self::FunctionWrongArgs(args, name) => write!(f, "Wrong args for function \"{name}\":{}", args.iter().map(|v| format!(" {v}")).collect::()), + Self::FunctionWrongArgs(fn_name, possible_fns, given_types) => write!(f, "Wrong args for function \"{fn_name}\": {} (possible fns: {})", given_types.iter().map(|v| format!(" {v}")).collect::(), ""), Self::InvalidType { expected, found, @@ -164,7 +164,7 @@ impl FormatGs for ToRunnableError { #[derive(Clone)] struct LInfo { vars: HashMap>, VType)>, - fns: HashMap>, + fns: HashMap>>, } pub fn to_runnable( @@ -461,25 +461,37 @@ fn statement_adv( } } let arg_types: Vec<_> = rargs.iter().map(|v| v.out(ginfo)).collect(); - if let Some(func) = linfo.fns.get(v) { - if let Some(_out) = check_fn_args( - &arg_types, - &func - .input_output_map - .iter() - .map(|v| (v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned())) - .collect(), - ginfo, - ) { - RStatementEnum::FunctionCall(func.clone(), rargs) - } else { - return Err(ToRunnableError::FunctionWrongArgs(arg_types, v.to_owned())); + if let Some(funcs) = linfo.fns.get(v) { + 'find_func: { + for func in funcs.iter().rev() { + if let Some(_out) = check_fn_args( + &arg_types, + &func + .input_output_map + .iter() + .map(|v| { + (v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned()) + }) + .collect(), + ginfo, + ) { + break 'find_func RStatementEnum::FunctionCall( + Arc::clone(&func), + rargs, + ); + } + } + return Err(ToRunnableError::FunctionWrongArgs( + v.to_owned(), + funcs.iter().map(|v| Arc::clone(v)).collect(), + arg_types, + )); } } else { if let Some(builtin) = BuiltinFunction::get(v) { let arg_types = rargs.iter().map(|v| v.out(ginfo)).collect(); if builtin.can_take(&arg_types, ginfo) { - RStatementEnum::BuiltinFunction(builtin, rargs) + RStatementEnum::BuiltinFunctionCall(builtin, rargs) } else { return Err(ToRunnableError::WrongInputsForBuiltinFunction( builtin, @@ -493,7 +505,7 @@ fn statement_adv( let lib = &ginfo.libs[*libid]; let libfn = &lib.registered_fns[*fnid]; if let Some(fn_out) = check_fn_args(&arg_types, &libfn.1, ginfo) { - RStatementEnum::LibFunction(*libid, *fnid, rargs, fn_out.clone()) + RStatementEnum::LibFunctionCall(*libid, *fnid, rargs, fn_out.clone()) } else { return Err(ToRunnableError::WrongArgsForLibFunction( v.to_owned(), @@ -507,18 +519,17 @@ fn statement_adv( } } SStatementEnum::FunctionDefinition(name, f) => { + let f = Arc::new(function(f, ginfo, linfo.clone())?); if let Some(name) = name { // named function => add to global functions - linfo - .fns - .insert(name.clone(), Arc::new(function(f, ginfo, linfo.clone())?)); - RStatementEnum::Value(VDataEnum::Tuple(vec![]).to()) - } else { - // anonymous function => return as value - RStatementEnum::Value( - VDataEnum::Function(Arc::new(function(f, ginfo, linfo.clone())?)).to(), - ) + let f = Arc::clone(&f); + if let Some(vec) = linfo.fns.get_mut(name) { + vec.push(f); + } else { + linfo.fns.insert(name.clone(), vec![f]); + } } + RStatementEnum::Value(VDataEnum::Function(f).to()) } SStatementEnum::Block(b) => RStatementEnum::Block(block(&b, ginfo, linfo.clone())?), SStatementEnum::If(c, t, e) => RStatementEnum::If(