there can now be multiple functions with the same name. The most recently declared one will be preferred if multiple would be valid.

This commit is contained in:
mark 2023-06-04 20:07:50 +02:00
parent c2594ab6dc
commit c7642ef911
4 changed files with 201 additions and 33 deletions

130
docs/statements.md Normal file
View File

@ -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: <statement>`
# 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"

View File

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

View File

@ -19,8 +19,8 @@ pub enum RStatementEnum {
List(Vec<RStatement>), List(Vec<RStatement>),
Variable(Arc<Mutex<VData>>, VType, bool), Variable(Arc<Mutex<VData>>, VType, bool),
FunctionCall(Arc<RFunction>, Vec<RStatement>), FunctionCall(Arc<RFunction>, Vec<RStatement>),
BuiltinFunction(BuiltinFunction, Vec<RStatement>), BuiltinFunctionCall(BuiltinFunction, Vec<RStatement>),
LibFunction(usize, usize, Vec<RStatement>, VType), LibFunctionCall(usize, usize, Vec<RStatement>, VType),
Block(RBlock), Block(RBlock),
If(RStatement, RStatement, Option<RStatement>), If(RStatement, RStatement, Option<RStatement>),
Loop(RStatement), Loop(RStatement),
@ -186,8 +186,8 @@ impl RStatementEnum {
} }
func.run(info) func.run(info)
} }
Self::BuiltinFunction(v, args) => v.run(args, info), Self::BuiltinFunctionCall(v, args) => v.run(args, info),
Self::LibFunction(libid, fnid, args, _) => { Self::LibFunctionCall(libid, fnid, args, _) => {
info.libs[*libid].run_fn(*fnid, args.iter().map(|arg| arg.run(info)).collect()) info.libs[*libid].run_fn(*fnid, args.iter().map(|arg| arg.run(info)).collect())
} }
Self::Block(b) => b.run(info), Self::Block(b) => b.run(info),
@ -343,7 +343,7 @@ impl RStatementEnum {
Self::FunctionCall(f, args) => { Self::FunctionCall(f, args) => {
f.out_vt(&args.iter().map(|v| v.out(info)).collect(), info) 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::Block(b) => b.out(info),
Self::If(_, a, b) => { Self::If(_, a, b) => {
if let Some(b) = b { if let Some(b) = b {
@ -354,7 +354,7 @@ impl RStatementEnum {
} }
Self::Loop(c) => c.out(info).matches().1, Self::Loop(c) => c.out(info).matches().1,
Self::For(_, _, b) => VSingleType::Tuple(vec![]).to() | b.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) f.returns(args.iter().map(|rs| rs.out(info)).collect(), info)
} }
Self::Switch(switch_on, cases, force) => { Self::Switch(switch_on, cases, force) => {

View File

@ -33,7 +33,7 @@ pub enum ToRunnableError {
CannotDeclareVariableWithDereference(String), CannotDeclareVariableWithDereference(String),
CannotDereferenceTypeNTimes(VType, usize, VType), CannotDereferenceTypeNTimes(VType, usize, VType),
FunctionWrongArgCount(String, usize, usize), FunctionWrongArgCount(String, usize, usize),
FunctionWrongArgs(Vec<VType>, String), FunctionWrongArgs(String, Vec<Arc<RFunction>>, Vec<VType>),
InvalidType { InvalidType {
expected: VType, expected: VType,
found: VType, found: VType,
@ -87,7 +87,7 @@ impl FormatGs for ToRunnableError {
Ok(()) Ok(())
}, },
Self::FunctionWrongArgCount(v, a, b) => write!(f, "Tried to call function \"{v}\", which takes {a} arguments, with {b} arguments instead."), 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::<String>()), 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::<String>(), ""),
Self::InvalidType { Self::InvalidType {
expected, expected,
found, found,
@ -164,7 +164,7 @@ impl FormatGs for ToRunnableError {
#[derive(Clone)] #[derive(Clone)]
struct LInfo { struct LInfo {
vars: HashMap<String, (Arc<Mutex<VData>>, VType)>, vars: HashMap<String, (Arc<Mutex<VData>>, VType)>,
fns: HashMap<String, Arc<RFunction>>, fns: HashMap<String, Vec<Arc<RFunction>>>,
} }
pub fn to_runnable( pub fn to_runnable(
@ -461,25 +461,37 @@ fn statement_adv(
} }
} }
let arg_types: Vec<_> = rargs.iter().map(|v| v.out(ginfo)).collect(); let arg_types: Vec<_> = rargs.iter().map(|v| v.out(ginfo)).collect();
if let Some(func) = linfo.fns.get(v) { if let Some(funcs) = linfo.fns.get(v) {
'find_func: {
for func in funcs.iter().rev() {
if let Some(_out) = check_fn_args( if let Some(_out) = check_fn_args(
&arg_types, &arg_types,
&func &func
.input_output_map .input_output_map
.iter() .iter()
.map(|v| (v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned())) .map(|v| {
(v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned())
})
.collect(), .collect(),
ginfo, ginfo,
) { ) {
RStatementEnum::FunctionCall(func.clone(), rargs) break 'find_func RStatementEnum::FunctionCall(
} else { Arc::clone(&func),
return Err(ToRunnableError::FunctionWrongArgs(arg_types, v.to_owned())); rargs,
);
}
}
return Err(ToRunnableError::FunctionWrongArgs(
v.to_owned(),
funcs.iter().map(|v| Arc::clone(v)).collect(),
arg_types,
));
} }
} else { } else {
if let Some(builtin) = BuiltinFunction::get(v) { if let Some(builtin) = BuiltinFunction::get(v) {
let arg_types = rargs.iter().map(|v| v.out(ginfo)).collect(); let arg_types = rargs.iter().map(|v| v.out(ginfo)).collect();
if builtin.can_take(&arg_types, ginfo) { if builtin.can_take(&arg_types, ginfo) {
RStatementEnum::BuiltinFunction(builtin, rargs) RStatementEnum::BuiltinFunctionCall(builtin, rargs)
} else { } else {
return Err(ToRunnableError::WrongInputsForBuiltinFunction( return Err(ToRunnableError::WrongInputsForBuiltinFunction(
builtin, builtin,
@ -493,7 +505,7 @@ fn statement_adv(
let lib = &ginfo.libs[*libid]; let lib = &ginfo.libs[*libid];
let libfn = &lib.registered_fns[*fnid]; let libfn = &lib.registered_fns[*fnid];
if let Some(fn_out) = check_fn_args(&arg_types, &libfn.1, ginfo) { 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 { } else {
return Err(ToRunnableError::WrongArgsForLibFunction( return Err(ToRunnableError::WrongArgsForLibFunction(
v.to_owned(), v.to_owned(),
@ -507,19 +519,18 @@ fn statement_adv(
} }
} }
SStatementEnum::FunctionDefinition(name, f) => { SStatementEnum::FunctionDefinition(name, f) => {
let f = Arc::new(function(f, ginfo, linfo.clone())?);
if let Some(name) = name { if let Some(name) = name {
// named function => add to global functions // named function => add to global functions
linfo let f = Arc::clone(&f);
.fns if let Some(vec) = linfo.fns.get_mut(name) {
.insert(name.clone(), Arc::new(function(f, ginfo, linfo.clone())?)); vec.push(f);
RStatementEnum::Value(VDataEnum::Tuple(vec![]).to())
} else { } else {
// anonymous function => return as value linfo.fns.insert(name.clone(), vec![f]);
RStatementEnum::Value(
VDataEnum::Function(Arc::new(function(f, ginfo, linfo.clone())?)).to(),
)
} }
} }
RStatementEnum::Value(VDataEnum::Function(f).to())
}
SStatementEnum::Block(b) => RStatementEnum::Block(block(&b, ginfo, linfo.clone())?), SStatementEnum::Block(b) => RStatementEnum::Block(block(&b, ginfo, linfo.clone())?),
SStatementEnum::If(c, t, e) => RStatementEnum::If( SStatementEnum::If(c, t, e) => RStatementEnum::If(
{ {