mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 14:13:52 +01:00
add examples, prepare for new readme
This commit is contained in:
parent
d124bff77f
commit
0c87c69743
@ -1,93 +0,0 @@
|
||||
# mers
|
||||
|
||||
Mers is a high-level programming language.
|
||||
It is designed to be safe (it doesn't crash at runtime) and as simple as possible.
|
||||
|
||||
Install from *crates.io*:
|
||||
|
||||
```sh
|
||||
cargo install mers
|
||||
```
|
||||
|
||||
See also:
|
||||
[Quickstart](Quickstart.md)
|
||||
|
||||
## what makes it special
|
||||
|
||||
### Simplicity
|
||||
|
||||
Mers is simple. There are only few expressions:
|
||||
|
||||
- Values (`1`, `"my string"`, ...)
|
||||
- Blocks (`{}`)
|
||||
- Tuples (`()`) and Objects (`{}`)
|
||||
- Assignments (`=`)
|
||||
- Variable initializations (`:=`)
|
||||
- Variables (`my_var`, `&my_var`)
|
||||
- If statements (`if <condition> <then> [else <else>]`)
|
||||
- Functions (`arg -> <do something>`)
|
||||
- Function calls `arg.function` or `arg1.function(arg2, arg3)` (= `(arg1, arg2, arg3).function`)
|
||||
|
||||
Everything else is implemented as a function.
|
||||
|
||||
### Types and Safety
|
||||
|
||||
Mers is built around a type-system where a value could be one of multiple types.
|
||||
```
|
||||
x := if condition { 12 } else { "something went wrong" }
|
||||
```
|
||||
|
||||
In mers, the compiler tracks all the types in your program,
|
||||
and it will catch every possible crash before the program even runs:
|
||||
If we tried to use `x` as an int, the compiler would complain since it might be a string, so this **does not compile**:
|
||||
|
||||
```
|
||||
list := (1, 2, if true 3 else "not an int")
|
||||
list.sum.println
|
||||
```
|
||||
|
||||
Type-safety for functions is different from what you might expect.
|
||||
You don't need to tell mers what type your function's argument has - you just use it however you want as if mers was a dynamically typed language:
|
||||
|
||||
```
|
||||
sum_doubled := iter -> {
|
||||
one := iter.sum
|
||||
(one, one).sum
|
||||
}
|
||||
(1, 2, 3).sum_doubled.println
|
||||
```
|
||||
|
||||
We could try to use the function improperly by passing a string instead of an int:
|
||||
|
||||
```
|
||||
(1, 2, "3").sum_doubled.println
|
||||
```
|
||||
|
||||
But mers will catch this and show an error, because the call to `sum` inside of `sum_doubled` would fail.
|
||||
|
||||
### Error Handling
|
||||
|
||||
Errors in mers are normal values.
|
||||
For example, `("ls", ("/")).run_command` has the return type `({Int/()}, String, String)/RunCommandError`.
|
||||
This means it either returns the result of the command (exit code, stdout, stderr) or an error (a value of type `RunCommandError`).
|
||||
|
||||
So, if we want to print the programs stdout, we could try
|
||||
|
||||
```
|
||||
(s, stdout, stderr) := ("ls", ("/")).run_command
|
||||
stdout.println
|
||||
```
|
||||
|
||||
But if we encountered a `RunCommandError`, mers wouldn't be able to assign the value to `(s, stdout, stderr)`, so this doesn't compile.
|
||||
Instead, we need to handle the error case, using the `try` function:
|
||||
|
||||
```
|
||||
("ls", ("/")).run_command.try((
|
||||
(s, stdout, stderr) -> stdout.println,
|
||||
error -> error.println,
|
||||
))
|
||||
```
|
||||
|
||||
## docs
|
||||
|
||||
docs will be available in some time. for now, check mers_lib/src/program/configs/*
|
@ -1,93 +1,7 @@
|
||||
# mers
|
||||
# mers-lib
|
||||
|
||||
Mers is a high-level programming language.
|
||||
It is designed to be safe (it doesn't crash at runtime) and as simple as possible.
|
||||
The library behind [mers](https://github.com/Dummi26/mers).
|
||||
|
||||
Install from *crates.io*:
|
||||
With this, you can parse, compile, check and run mers code.
|
||||
|
||||
```sh
|
||||
cargo install mers
|
||||
```
|
||||
|
||||
See also:
|
||||
[Quickstart](Quickstart.md)
|
||||
|
||||
## what makes it special
|
||||
|
||||
### Simplicity
|
||||
|
||||
Mers is simple. There are only few expressions:
|
||||
|
||||
- Values (`1`, `"my string"`, ...)
|
||||
- Blocks (`{}`)
|
||||
- Tuples (`()`) and Objects (`{}`)
|
||||
- Assignments (`=`)
|
||||
- Variable initializations (`:=`)
|
||||
- Variables (`my_var`, `&my_var`)
|
||||
- If statements (`if <condition> <then> [else <else>]`)
|
||||
- Functions (`arg -> <do something>`)
|
||||
- Function calls `arg.function` or `arg1.function(arg2, arg3)` (= `(arg1, arg2, arg3).function`)
|
||||
|
||||
Everything else is implemented as a function.
|
||||
|
||||
### Types and Safety
|
||||
|
||||
Mers is built around a type-system where a value could be one of multiple types.
|
||||
```
|
||||
x := if condition { 12 } else { "something went wrong" }
|
||||
```
|
||||
|
||||
In mers, the compiler tracks all the types in your program,
|
||||
and it will catch every possible crash before the program even runs:
|
||||
If we tried to use `x` as an int, the compiler would complain since it might be a string, so this **does not compile**:
|
||||
|
||||
```
|
||||
list := (1, 2, if true 3 else "not an int")
|
||||
list.sum.println
|
||||
```
|
||||
|
||||
Type-safety for functions is different from what you might expect.
|
||||
You don't need to tell mers what type your function's argument has - you just use it however you want as if mers was a dynamically typed language:
|
||||
|
||||
```
|
||||
sum_doubled := iter -> {
|
||||
one := iter.sum
|
||||
(one, one).sum
|
||||
}
|
||||
(1, 2, 3).sum_doubled.println
|
||||
```
|
||||
|
||||
We could try to use the function improperly by passing a string instead of an int:
|
||||
|
||||
```
|
||||
(1, 2, "3").sum_doubled.println
|
||||
```
|
||||
|
||||
But mers will catch this and show an error, because the call to `sum` inside of `sum_doubled` would fail.
|
||||
|
||||
### Error Handling
|
||||
|
||||
Errors in mers are normal values.
|
||||
For example, `("ls", ("/")).run_command` has the return type `({Int/()}, String, String)/RunCommandError`.
|
||||
This means it either returns the result of the command (exit code, stdout, stderr) or an error (a value of type `RunCommandError`).
|
||||
|
||||
So, if we want to print the programs stdout, we could try
|
||||
|
||||
```
|
||||
(s, stdout, stderr) := ("ls", ("/")).run_command
|
||||
stdout.println
|
||||
```
|
||||
|
||||
But if we encountered a `RunCommandError`, mers wouldn't be able to assign the value to `(s, stdout, stderr)`, so this doesn't compile.
|
||||
Instead, we need to handle the error case, using the `try` function:
|
||||
|
||||
```
|
||||
("ls", ("/")).run_command.try((
|
||||
(s, stdout, stderr) -> stdout.println,
|
||||
error -> error.println,
|
||||
))
|
||||
```
|
||||
|
||||
## docs
|
||||
|
||||
docs will be available in some time. for now, check mers_lib/src/program/configs/*
|
||||
You can also add your own functions and types which can then be used from mers, if you really want to.
|
||||
|
@ -1,58 +0,0 @@
|
||||
# mers documentation
|
||||
|
||||
## ISSUES
|
||||
|
||||
when storing a reference, then reinitializing a variable of the same name, the reference may get the new value although
|
||||
it would be expected for it to be a reference to the value before it was reinitialized.
|
||||
|
||||
## parsing
|
||||
|
||||
syntax:
|
||||
|
||||
- `// <comment>`
|
||||
- `/* <comment> */`
|
||||
|
||||
operators:
|
||||
- `<target> := <source>` init
|
||||
- `<target> = <source>` assign (`<target>` must be a reference)
|
||||
- `+`
|
||||
- `-`
|
||||
- `*`
|
||||
- `/`
|
||||
- `%`
|
||||
- `&`
|
||||
- `|`
|
||||
- `&&`
|
||||
- `||`
|
||||
|
||||
keywords (must be between whitespace):
|
||||
|
||||
- `if <condition> <statement>`
|
||||
- `else <statement>` (after `if`)
|
||||
- `loop <statement>`
|
||||
- `switch { <arms> }`
|
||||
- `<arg> -> <statement>`
|
||||
- `def <name> <: for types, = for comptime> <_>` for compile-time stuff (types, macros, ...)
|
||||
|
||||
## details
|
||||
|
||||
### functions
|
||||
|
||||
A function takes an argument and returns some data:
|
||||
|
||||
func := input -> input + 2
|
||||
3.func.println // 5
|
||||
(list, 0).get // first element
|
||||
(val, match -> match.println, [] -> "doesn't match".println).match
|
||||
|
||||
### switch
|
||||
|
||||
switch <val> {
|
||||
<type> <func>
|
||||
}
|
||||
|
||||
switch something {
|
||||
int num -> {"int: " + num}.println
|
||||
float num -> {"float: " + num}.println
|
||||
}
|
||||
|
50
mers_lib/examples/00_parse_compile_check_run.rs
Normal file
50
mers_lib/examples/00_parse_compile_check_run.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use mers_lib::{
|
||||
data::{Data, MersType, Type},
|
||||
errors::CheckError,
|
||||
prelude_compile::{parse, CompInfo, Config, Source},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
show("1.sum(2)".to_owned());
|
||||
show("1.sum(2).println".to_owned());
|
||||
show("1.sum(2.5)".to_owned());
|
||||
show("if true { 1 } else { 0.5 }".to_owned());
|
||||
}
|
||||
|
||||
/// Tries to parse, compile, check and run `src`,
|
||||
/// then prints an error or the returned value and output type to stderr.
|
||||
/// Note: The output type is not the type of the value but the one determined by `.check()` before the code even runs.
|
||||
fn show(src: String) {
|
||||
eprintln!(
|
||||
"-{}",
|
||||
" -".repeat(src.lines().map(|l| l.len()).max().unwrap_or(0) / 2)
|
||||
);
|
||||
eprintln!("{src}");
|
||||
match parse_compile_check_run(src) {
|
||||
Err(e) => eprintln!("{e}"),
|
||||
Ok((t, v)) => eprintln!("Returned `{}` :: `{t}`", v.get()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_compile_check_run(src: String) -> Result<(Type, Data), CheckError> {
|
||||
// prepare the string for parsing
|
||||
let mut source = Source::new_from_string(src);
|
||||
// this is used for error messages
|
||||
let srca = Arc::new(source.clone());
|
||||
// parse the code
|
||||
let parsed = parse(&mut source, &srca)?;
|
||||
// get infos
|
||||
let (mut i1, mut i2, mut i3) = Config::new().bundle_std().infos();
|
||||
// compile
|
||||
let compiled = parsed.compile(&mut i1, CompInfo::default())?;
|
||||
// check (this step is optional, but if it is skipped when it would have returned an error, `run` will likely panic)
|
||||
let output_type = compiled.check(&mut i3, None)?;
|
||||
// run
|
||||
let output_value = compiled.run(&mut i2);
|
||||
// check that the predicted output type was correct
|
||||
assert!(output_value.get().as_type().is_included_in(&output_type));
|
||||
// return the produced value
|
||||
Ok((output_type, output_value))
|
||||
}
|
57
mers_lib/examples/01_user_scripts.rs
Normal file
57
mers_lib/examples/01_user_scripts.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use mers_lib::{
|
||||
data::{self, Data, MersType, Type},
|
||||
errors::CheckError,
|
||||
prelude_compile::{parse, CompInfo, Config, Source},
|
||||
};
|
||||
|
||||
fn main() -> Result<(), CheckError> {
|
||||
let (_, func) = parse_compile_check_run(
|
||||
// The `[(String -> String)]` type annotation ensures that decorate.mers returns a `String -> String` function.
|
||||
"[(String -> String)] #include \"examples/decorate.mers\"".to_owned(),
|
||||
)?;
|
||||
|
||||
// We can unwrap the downcasts because mers has type-checked that `func` is a `(String -> String)`.
|
||||
|
||||
let func = func.get();
|
||||
let func = func
|
||||
.as_any()
|
||||
.downcast_ref::<data::function::Function>()
|
||||
.unwrap();
|
||||
|
||||
// use the function to decorate these 3 test strings
|
||||
for input in ["my test string", "Main Menu", "O.o"] {
|
||||
let result = func.run(Data::new(data::string::String(input.to_owned())));
|
||||
let result = result.get();
|
||||
let result = &result
|
||||
.as_any()
|
||||
.downcast_ref::<data::string::String>()
|
||||
.unwrap()
|
||||
.0;
|
||||
eprintln!("{result}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_compile_check_run(src: String) -> Result<(Type, Data), CheckError> {
|
||||
// prepare the string for parsing
|
||||
let mut source = Source::new_from_string(src);
|
||||
// this is used for error messages
|
||||
let srca = Arc::new(source.clone());
|
||||
// parse the code
|
||||
let parsed = parse(&mut source, &srca)?;
|
||||
// get infos
|
||||
let (mut i1, mut i2, mut i3) = Config::new().bundle_std().infos();
|
||||
// compile
|
||||
let compiled = parsed.compile(&mut i1, CompInfo::default())?;
|
||||
// check (this step is optional, but if it is skipped when it would have returned an error, `run` will likely panic)
|
||||
let output_type = compiled.check(&mut i3, None)?;
|
||||
// run
|
||||
let output_value = compiled.run(&mut i2);
|
||||
// check that the predicted output type was correct
|
||||
assert!(output_value.get().as_type().is_included_in(&output_type));
|
||||
// return the produced value
|
||||
Ok((output_type, output_value))
|
||||
}
|
2
mers_lib/examples/decorate.mers
Normal file
2
mers_lib/examples/decorate.mers
Normal file
@ -0,0 +1,2 @@
|
||||
// return a function that adds "-= ... =-" decorations to the given text
|
||||
text -> ("-= ", text, " =-").concat
|
Loading…
Reference in New Issue
Block a user