diff --git a/README.md b/README.md index c303258..f1eb3ba 100644 --- a/README.md +++ b/README.md @@ -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 [else ]`) -- Functions (`arg -> `) -- 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 :: [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 :: [1, 2]` + +--- + +``` +(1, 2, 3.5).as_list.debug +``` + +> `List :: [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 :: [1, 2, 3, 4.5, 6]` diff --git a/mers_lib/Cargo.toml b/mers_lib/Cargo.toml index b265d4d..a229cf2 100755 --- a/mers_lib/Cargo.toml +++ b/mers_lib/Cargo.toml @@ -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" diff --git a/mers_lib/README.md b/mers_lib/README.md index 192792d..ff66954 100644 --- a/mers_lib/README.md +++ b/mers_lib/README.md @@ -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. diff --git a/mers_lib/examples/01_user_scripts.rs b/mers_lib/examples/01_user_scripts.rs index 8b5ea31..b32f040 100644 --- a/mers_lib/examples/01_user_scripts.rs +++ b/mers_lib/examples/01_user_scripts.rs @@ -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)) } diff --git a/mers_lib/src/data/function.rs b/mers_lib/src/data/function.rs index 13f4aa7..311ff09 100755 --- a/mers_lib/src/data/function.rs +++ b/mers_lib/src/data/function.rs @@ -23,6 +23,18 @@ pub struct Function { )>, } impl Function { + pub fn new( + out: impl Fn(&Type) -> Result + 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) -> Self { Self { info,