mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +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>),
|
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) => {
|
||||||
|
@ -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) {
|
||||||
if let Some(_out) = check_fn_args(
|
'find_func: {
|
||||||
&arg_types,
|
for func in funcs.iter().rev() {
|
||||||
&func
|
if let Some(_out) = check_fn_args(
|
||||||
.input_output_map
|
&arg_types,
|
||||||
.iter()
|
&func
|
||||||
.map(|v| (v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned()))
|
.input_output_map
|
||||||
.collect(),
|
.iter()
|
||||||
ginfo,
|
.map(|v| {
|
||||||
) {
|
(v.0.iter().map(|v| v.clone().to()).collect(), v.1.to_owned())
|
||||||
RStatementEnum::FunctionCall(func.clone(), rargs)
|
})
|
||||||
} else {
|
.collect(),
|
||||||
return Err(ToRunnableError::FunctionWrongArgs(arg_types, v.to_owned()));
|
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 {
|
} 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,18 +519,17 @@ 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 {
|
linfo.fns.insert(name.clone(), vec![f]);
|
||||||
// anonymous function => return as value
|
}
|
||||||
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(
|
||||||
|
Loading…
Reference in New Issue
Block a user