mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +01:00
mers_lib to 0.5.0, readme updated
This commit is contained in:
parent
0c87c69743
commit
cc4a4366c9
111
README.md
111
README.md
@ -1,93 +1,68 @@
|
|||||||
# mers
|
# mers
|
||||||
|
|
||||||
Mers is a high-level programming language.
|
See [mers/README.md]
|
||||||
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")
|
"Hello, World!".println
|
||||||
list.sum.println
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Type-safety for functions is different from what you might expect.
|
> `Hello, Variable!`
|
||||||
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 -> {
|
my_var := "Hello, Variable!"
|
||||||
one := iter.sum
|
my_var.println
|
||||||
(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:
|
> `Hello, Variable!`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
```
|
```
|
||||||
(1, 2, "3").sum_doubled.println
|
(1, 2, 3, 4).sum.println
|
||||||
```
|
```
|
||||||
|
|
||||||
But mers will catch this and show an error, because the call to `sum` inside of `sum_doubled` would fail.
|
> `10`
|
||||||
|
|
||||||
### 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
|
(1, "2", 3, 4).sum.println
|
||||||
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((
|
(1, 2, 3, 4).as_list.debug
|
||||||
(s, stdout, stderr) -> stdout.println,
|
|
||||||
error -> error.println,
|
|
||||||
))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## docs
|
> `List<Int> :: [1, 2, 3, 4]`
|
||||||
|
|
||||||
docs will be available in some time. for now, check mers_lib/src/program/configs/*
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
(1.0, 2.0).as_list.debug
|
||||||
|
```
|
||||||
|
|
||||||
|
> `List<Float> :: [1, 2]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
(1, 2, 3.5).as_list.debug
|
||||||
|
```
|
||||||
|
|
||||||
|
> `List<Int/Float> :: [1, 2, 3.5]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
int_list := (1, 2, 3).as_list
|
||||||
|
float_list := (4.5, 6.0).as_list
|
||||||
|
int_list.chain(float_list).as_list.debug
|
||||||
|
```
|
||||||
|
|
||||||
|
> `List<Int/Float> :: [1, 2, 3, 4.5, 6]`
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mers_lib"
|
name = "mers_lib"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "library to use the mers language in other projects"
|
description = "library to use the mers language in other projects"
|
||||||
|
@ -3,5 +3,47 @@
|
|||||||
The library behind [mers](https://github.com/Dummi26/mers).
|
The library behind [mers](https://github.com/Dummi26/mers).
|
||||||
|
|
||||||
With this, you can parse, compile, check and run mers code.
|
With this, you can parse, compile, check and run mers code.
|
||||||
|
|
||||||
You can also add your own functions and types which can then be used from mers, if you really want to.
|
You can also add your own functions and types which can then be used from mers, if you really want to.
|
||||||
|
|
||||||
|
## Running mers
|
||||||
|
|
||||||
|
There are four steps to running mers code.
|
||||||
|
The examples show you how to actually implement them,
|
||||||
|
this readme only explains what they do any why.
|
||||||
|
|
||||||
|
### 1. Parsing
|
||||||
|
|
||||||
|
This first step converts the source code, a string, to a parsed mers statement.
|
||||||
|
|
||||||
|
In this step, syntax errors and unknown variables are caught.
|
||||||
|
|
||||||
|
### 2. Compiling
|
||||||
|
|
||||||
|
This converts a parsed mers statement to a compiled one. It almost never produces an error.
|
||||||
|
|
||||||
|
### 3. Checking
|
||||||
|
|
||||||
|
This step is optional. If you parse and compile your source code, you can (try to) run it.
|
||||||
|
However, mers assumes that all mers code you run is actually valid,
|
||||||
|
so if you don't check your codes validity, mers will probably panic while running your code.
|
||||||
|
|
||||||
|
This step performs all the type-checking and determines the output type of your code, if it is valid.
|
||||||
|
|
||||||
|
For example, the following code is valid and has the return type `Int/Float`:
|
||||||
|
|
||||||
|
```
|
||||||
|
my_condition := true
|
||||||
|
|
||||||
|
if my_condition {
|
||||||
|
5
|
||||||
|
} else {
|
||||||
|
1.4
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Running
|
||||||
|
|
||||||
|
This step assumes that the code it is running is actually valid, so it never returns an error.
|
||||||
|
As long as `check` didn't return an error in Step 3, it is safe to assume that this will return the value produced by the code.
|
||||||
|
We can also assume that the return value has a type which is included in that determined by `check`.
|
||||||
|
If `check` returned an error, this will likely panic.
|
||||||
|
@ -35,23 +35,14 @@ fn main() -> Result<(), CheckError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// example 00
|
||||||
fn parse_compile_check_run(src: String) -> Result<(Type, Data), CheckError> {
|
fn parse_compile_check_run(src: String) -> Result<(Type, Data), CheckError> {
|
||||||
// prepare the string for parsing
|
|
||||||
let mut source = Source::new_from_string(src);
|
let mut source = Source::new_from_string(src);
|
||||||
// this is used for error messages
|
|
||||||
let srca = Arc::new(source.clone());
|
let srca = Arc::new(source.clone());
|
||||||
// parse the code
|
|
||||||
let parsed = parse(&mut source, &srca)?;
|
let parsed = parse(&mut source, &srca)?;
|
||||||
// get infos
|
|
||||||
let (mut i1, mut i2, mut i3) = Config::new().bundle_std().infos();
|
let (mut i1, mut i2, mut i3) = Config::new().bundle_std().infos();
|
||||||
// compile
|
|
||||||
let compiled = parsed.compile(&mut i1, CompInfo::default())?;
|
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)?;
|
let output_type = compiled.check(&mut i3, None)?;
|
||||||
// run
|
|
||||||
let output_value = compiled.run(&mut i2);
|
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))
|
Ok((output_type, output_value))
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,18 @@ pub struct Function {
|
|||||||
)>,
|
)>,
|
||||||
}
|
}
|
||||||
impl Function {
|
impl Function {
|
||||||
|
pub fn new(
|
||||||
|
out: impl Fn(&Type) -> Result<Type, CheckError> + Send + Sync + 'static,
|
||||||
|
run: impl Fn(Data) -> Data + Send + Sync + 'static,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
info: Arc::new(crate::info::Info::neverused()),
|
||||||
|
info_check: Arc::new(Mutex::new(crate::info::Info::neverused())),
|
||||||
|
out: Arc::new(move |a, _| out(a)),
|
||||||
|
run: Arc::new(move |a, _| run(a)),
|
||||||
|
inner_statements: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn with_info_run(&self, info: Arc<Info>) -> Self {
|
pub fn with_info_run(&self, info: Arc<Info>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
info,
|
info,
|
||||||
|
Loading…
Reference in New Issue
Block a user