From 0c87c69743e3c5d3fdf9e3a4e2cde7ffeae9b132 Mon Sep 17 00:00:00 2001 From: Mark <> Date: Sat, 17 Feb 2024 13:23:50 +0100 Subject: [PATCH] add examples, prepare for new readme --- mers/README.md | 93 ------------------ mers_lib/README.md | 94 +------------------ mers_lib/doc.md | 58 ------------ .../examples/00_parse_compile_check_run.rs | 50 ++++++++++ mers_lib/examples/01_user_scripts.rs | 57 +++++++++++ mers_lib/examples/decorate.mers | 2 + 6 files changed, 113 insertions(+), 241 deletions(-) delete mode 100644 mers/README.md delete mode 100755 mers_lib/doc.md create mode 100644 mers_lib/examples/00_parse_compile_check_run.rs create mode 100644 mers_lib/examples/01_user_scripts.rs create mode 100644 mers_lib/examples/decorate.mers diff --git a/mers/README.md b/mers/README.md deleted file mode 100644 index c303258..0000000 --- a/mers/README.md +++ /dev/null @@ -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 [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 -``` - -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/* diff --git a/mers_lib/README.md b/mers_lib/README.md index c303258..192792d 100644 --- a/mers_lib/README.md +++ b/mers_lib/README.md @@ -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 [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 -``` - -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. diff --git a/mers_lib/doc.md b/mers_lib/doc.md deleted file mode 100755 index ec4381c..0000000 --- a/mers_lib/doc.md +++ /dev/null @@ -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: - -- `// ` -- `/* */` - -operators: -- ` := ` init -- ` = ` assign (`` must be a reference) -- `+` -- `-` -- `*` -- `/` -- `%` -- `&` -- `|` -- `&&` -- `||` - -keywords (must be between whitespace): - -- `if ` -- `else ` (after `if`) -- `loop ` -- `switch { }` -- ` -> ` -- `def <: 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 { - - } - - switch something { - int num -> {"int: " + num}.println - float num -> {"float: " + num}.println - } - diff --git a/mers_lib/examples/00_parse_compile_check_run.rs b/mers_lib/examples/00_parse_compile_check_run.rs new file mode 100644 index 0000000..46c8e39 --- /dev/null +++ b/mers_lib/examples/00_parse_compile_check_run.rs @@ -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)) +} diff --git a/mers_lib/examples/01_user_scripts.rs b/mers_lib/examples/01_user_scripts.rs new file mode 100644 index 0000000..8b5ea31 --- /dev/null +++ b/mers_lib/examples/01_user_scripts.rs @@ -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::() + .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::() + .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)) +} diff --git a/mers_lib/examples/decorate.mers b/mers_lib/examples/decorate.mers new file mode 100644 index 0000000..34e2424 --- /dev/null +++ b/mers_lib/examples/decorate.mers @@ -0,0 +1,2 @@ +// return a function that adds "-= ... =-" decorations to the given text +text -> ("-= ", text, " =-").concat