mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +01:00
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:
parent
c2594ab6dc
commit
c7642ef911
130
docs/statements.md
Normal file
130
docs/statements.md
Normal 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"
|
27
examples/functions_double_definitions.mers
Normal file
27
examples/functions_double_definitions.mers
Normal 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()
|
||||
)
|
@ -19,8 +19,8 @@ pub enum RStatementEnum {
|
||||
List(Vec<RStatement>),
|
||||
Variable(Arc<Mutex<VData>>, VType, bool),
|
||||
FunctionCall(Arc<RFunction>, Vec<RStatement>),
|
||||
BuiltinFunction(BuiltinFunction, Vec<RStatement>),
|
||||
LibFunction(usize, usize, Vec<RStatement>, VType),
|
||||
BuiltinFunctionCall(BuiltinFunction, Vec<RStatement>),
|
||||
LibFunctionCall(usize, usize, Vec<RStatement>, VType),
|
||||
Block(RBlock),
|
||||
If(RStatement, RStatement, Option<RStatement>),
|
||||
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) => {
|
||||
|
@ -33,7 +33,7 @@ pub enum ToRunnableError {
|
||||
CannotDeclareVariableWithDereference(String),
|
||||
CannotDereferenceTypeNTimes(VType, usize, VType),
|
||||
FunctionWrongArgCount(String, usize, usize),
|
||||
FunctionWrongArgs(Vec<VType>, String),
|
||||
FunctionWrongArgs(String, Vec<Arc<RFunction>>, Vec<VType>),
|
||||
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::<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 {
|
||||
expected,
|
||||
found,
|
||||
@ -164,7 +164,7 @@ impl FormatGs for ToRunnableError {
|
||||
#[derive(Clone)]
|
||||
struct LInfo {
|
||||
vars: HashMap<String, (Arc<Mutex<VData>>, VType)>,
|
||||
fns: HashMap<String, Arc<RFunction>>,
|
||||
fns: HashMap<String, Vec<Arc<RFunction>>>,
|
||||
}
|
||||
|
||||
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(
|
||||
|
Loading…
Reference in New Issue
Block a user