mers_lib to 0.5.0, readme updated

This commit is contained in:
Mark 2024-02-17 14:06:19 +01:00
parent 0c87c69743
commit cc4a4366c9
5 changed files with 100 additions and 80 deletions

111
README.md
View File

@ -1,93 +1,68 @@
# 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.
See [mers/README.md]
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
"Hello, World!".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:
> `Hello, Variable!`
---
```
sum_doubled := iter -> {
one := iter.sum
(one, one).sum
}
(1, 2, 3).sum_doubled.println
my_var := "Hello, Variable!"
my_var.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
stdout.println
(1, "2", 3, 4).sum.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,
))
(1, 2, 3, 4).as_list.debug
```
## 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]`

View File

@ -1,6 +1,6 @@
[package]
name = "mers_lib"
version = "0.4.0"
version = "0.5.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "library to use the mers language in other projects"

View File

@ -3,5 +3,47 @@
The library behind [mers](https://github.com/Dummi26/mers).
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.
## 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.

View File

@ -35,23 +35,14 @@ fn main() -> Result<(), CheckError> {
Ok(())
}
/// example 00
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))
}

View File

@ -23,6 +23,18 @@ pub struct 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 {
Self {
info,