mirror of
				https://github.com/Dummi26/mers.git
				synced 2025-11-04 13:21:26 +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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user