mirror of
https://github.com/Dummi26/mers.git
synced 2025-12-15 19:47:50 +01:00
move old mers to old/ and update gitignore
This commit is contained in:
175
old/docs/builtins.md
Executable file
175
old/docs/builtins.md
Executable file
@@ -0,0 +1,175 @@
|
||||
# mers builtins
|
||||
|
||||
## functions
|
||||
|
||||
### assume1
|
||||
|
||||
if the input is `[v]`, returns `v`. if the input is `[]`, panics and crashes the program.
|
||||
useful for prototyping when error-handling is not a priority or for use with the `get` function with an index that is always in range.
|
||||
|
||||
### assume_no_enum
|
||||
|
||||
if the input is any enum variant, panics and crashes the program.
|
||||
just like `assume1`, this is useful to quickly ignore `Err(_)` types.
|
||||
|
||||
### noenum
|
||||
|
||||
if the input is any enum variant, returns the inner value. mostly used in `switch` statements when you want to use the inner value from an enum value (like the `string` from an `Err(string)`).
|
||||
|
||||
### matches
|
||||
|
||||
returns `[]` for values that don't match and `[v]` for ones that do, where `v` is the matched value.
|
||||
useful to avoid the (sometimes ambiguous) values that technically match but aren't `[v]`.
|
||||
|
||||
### clone
|
||||
|
||||
NOTE: may be replaced with dereference syntax?
|
||||
|
||||
### print
|
||||
|
||||
writes a string to stdout
|
||||
|
||||
### println
|
||||
|
||||
like print, but adds a newline character
|
||||
|
||||
### debug
|
||||
|
||||
prints a values compile-time type, runtime type and value
|
||||
|
||||
### read_line
|
||||
|
||||
reads one line from stdin and returns it.
|
||||
blocks until input was received.
|
||||
|
||||
### to_string
|
||||
|
||||
converts any value to a string so that the string, if parsed by the mers parser, would perfectly represent the valu.
|
||||
Exceptions are strings (because of escape sequences), functions, thread values and maybe more. this list should get shorter with time.
|
||||
|
||||
### format
|
||||
|
||||
given a format string (first argument) and any additional number of strings, replaces "{n}" in the format string with the {n}th additional argument,
|
||||
so that {0} represents the first extra argument: `"val: {0}".format(value)`
|
||||
|
||||
### parse_int
|
||||
|
||||
tries to parse a string to an int
|
||||
|
||||
### parse_float
|
||||
|
||||
tries to parse a string to a float
|
||||
|
||||
### run
|
||||
|
||||
runs an anonymous function. further arguments are passed to the anonymous function.
|
||||
|
||||
### thread
|
||||
|
||||
like run, but spawns a new thread to do the work.
|
||||
|
||||
### await
|
||||
|
||||
takes the value returned by thread, waits for the thread to finish and then returns whatever the anonymous function used to spawn the thread returned.
|
||||
|
||||
### sleep
|
||||
|
||||
sleeps for a number of seconds (int/float)
|
||||
|
||||
### exit
|
||||
|
||||
exits the process, optionally with a specific exit code
|
||||
|
||||
### fs_list, fs_read, fs_write
|
||||
|
||||
file system operations - will likely be reworked at some point
|
||||
|
||||
### bytes_to_string, string_to_bytes
|
||||
|
||||
converts UTF-8 bytes to a string (can error) and back (can't error)
|
||||
|
||||
### run_command, run_command_get_bytes
|
||||
|
||||
runs a command (executable in PATH) with a set of arguments (list of string), returning `[exit_code stdout stderr]` on success
|
||||
|
||||
### not
|
||||
|
||||
turns `true` to `false` and `false` to `true`
|
||||
|
||||
### and, or, add, sub, mul, div, mod, pow, eq, ne, lt, gt, ltoe, gtoe
|
||||
|
||||
functions for operators like `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `>`, `<=`, ...
|
||||
|
||||
### min, max
|
||||
|
||||
returns the max/min of two numbers
|
||||
|
||||
### push
|
||||
|
||||
given a reference to a list and some value, appends that value to the end of the list.
|
||||
|
||||
### insert
|
||||
|
||||
same as push, but the index where the value should be inserted can be specified
|
||||
|
||||
### pop
|
||||
|
||||
given a reference to a list, <removes and returns the last element from a list<
|
||||
|
||||
### remove
|
||||
|
||||
same as pop, but the index of the value to remove can be specified
|
||||
|
||||
### get
|
||||
|
||||
given a list and an index, returns the value at that index wrapped in a 1-length tuple or `[]`.
|
||||
if the first argument is a refernce to a list, this will return a reference to the value at that index (which can be modified):
|
||||
|
||||
`&list.get(2).assume1() = "new_value"`
|
||||
|
||||
### len
|
||||
|
||||
returns the length of the string/list/tuple/...
|
||||
|
||||
### contains, starts_with, ends_with
|
||||
|
||||
check for certain substring in a string
|
||||
|
||||
### index_or
|
||||
|
||||
find first index of a certain substring in a string, or return `[]` otherwise
|
||||
|
||||
### trim
|
||||
|
||||
remove leading and trailing whitespaces from the string
|
||||
|
||||
### substring
|
||||
|
||||
returns a sustring of the original string.
|
||||
|
||||
first argmuent is the start index. -1 is the last character in the string, -2 the second to last and so on.
|
||||
|
||||
second argument, if provided, is the end index (exclusive). if it is negative, it limits the string's length: `"1234".substring(1, -2)` returns `"23"`.
|
||||
|
||||
### replace
|
||||
|
||||
replaces occurences of arg1 in arg0 with arg2
|
||||
|
||||
### regex
|
||||
|
||||
given a regex (in string form), this function returns either `Err(string)` or a function which, when called with another string, returns a list of matches found in that string:
|
||||
|
||||
lines_regex := regex(".*").assume_no_enum()
|
||||
fn lines(s string) {
|
||||
lines_regex.run(s)
|
||||
}
|
||||
debug("a string\nwith multiple\nlines!".lines())
|
||||
|
||||
This is done because compiling regex is somewhat slow, so if multiple strings have to be searched by the regex,
|
||||
it would be inefficient to recompile the regex every time. (btw: credit goes to the Regex crate, which is used to implement this)
|
||||
|
||||
### split
|
||||
|
||||
given two strings, splits the first one at the pattern specified by the second one:
|
||||
|
||||
word_count := "a string containing five words".split(" ").len()
|
||||
230
old/docs/intro.md
Executable file
230
old/docs/intro.md
Executable file
@@ -0,0 +1,230 @@
|
||||
# Hello, world!
|
||||
|
||||
Welcome to mers! Start by creating a file for your mers code:
|
||||
|
||||
println("Hello, world!")
|
||||
|
||||
now run it: `mers <file>` (replace mers with the location of the compiled executable if it's not in your PATH)
|
||||
|
||||
# The basics
|
||||
|
||||
To write a comment, type `//`. From here until the end of the line, whatever you write will be ignored completely.
|
||||
To write a comment that spans multiple lines or ends before the end of the line, put your text between `/*` and `*/`.
|
||||
|
||||
if 10 != 15 /* pretend this actually meant something... */ {
|
||||
// does something
|
||||
} else {
|
||||
// do something else
|
||||
}
|
||||
|
||||
To declare a new variable, use `:=`.
|
||||
|
||||
println("Hello! Please enter your name.")
|
||||
username := read_line()
|
||||
println("Hello, " + username)
|
||||
|
||||
To change an existing variable, prefix it with `&` and use `=` instead of `:=`.
|
||||
|
||||
username := "<unknown>"
|
||||
println("Hello! Please enter your name.")
|
||||
&username = read_line()
|
||||
println("Hello, " + username)
|
||||
|
||||
To call a function, write `function_name(argument1, argument2, argument3, ...)` with as many arguments as you need:
|
||||
|
||||
add(10, 25)
|
||||
|
||||
To avoid too many nested function calls, you can also put the first argument in front of the function and have a dot behind it:
|
||||
|
||||
10.add(25)
|
||||
|
||||
The following two lines are identical:
|
||||
|
||||
println(to_string(add(10, 25)))
|
||||
10.add(25).to_string().println()
|
||||
|
||||
# Conditions - If/Else
|
||||
|
||||
An `if`-statement is used to only do something if a condition is met.
|
||||
In mers, no brackets are necessary to do this.
|
||||
You can just write `if <any condition> <what to do>`.
|
||||
|
||||
println("Hello! Please enter your name.")
|
||||
username := read_line()
|
||||
if username.len() > 0 {
|
||||
println("Hello, " + username)
|
||||
}
|
||||
|
||||
You may have noticed that the `{` and `}` shown in the example weren't mentioned before.
|
||||
That's because they are totally optional! If you only want to do one thing, like the `println` in our case, you can leave them out. However, this can get messy very quickly.
|
||||
|
||||
println("Hello! Please enter your name.")
|
||||
username := read_line()
|
||||
if username.len() > 0
|
||||
println("Hello, " + username)
|
||||
|
||||
The indentation here also isn't required - mers doesn't care about indentation. A single space is as good as any number of spaces, tabs, or newline characters.
|
||||
|
||||
By adding `else <what to do>` after an if statement, we can define what should happen if the condition isn't met:
|
||||
|
||||
println("Hello! Please enter your name.")
|
||||
username := read_line()
|
||||
if username.len() > 0 {
|
||||
println("Hello, " + username)
|
||||
} else {
|
||||
println("Hello!")
|
||||
}
|
||||
|
||||
# Loops - Loop
|
||||
|
||||
Let's force the user to give us their name.
|
||||
|
||||
For this, we'll do what any not-insane person would do: Ask them repeatedly until they answer.
|
||||
|
||||
username := ""
|
||||
loop {
|
||||
println("Hello! Please enter your name.")
|
||||
&username = read_line()
|
||||
if username.len() > 0 {
|
||||
true
|
||||
}
|
||||
}
|
||||
println("Hello, " + username)
|
||||
|
||||
The most obvious thing that stands out here is `loop`. This just repeatedly runs its statement.
|
||||
Since mers doesn't have `break`s or `return`s, we need a different way to exit the loop.
|
||||
This is done by returning `true` from the block, which is exactly what the if statement is doing.
|
||||
|
||||
We can simplify this by just removing the `if` entirely - if `username.len() > 0` returns `true`, we break from the loop, if it returns `false` we don't.
|
||||
|
||||
username := ""
|
||||
loop {
|
||||
println("Hello! Please enter your name.")
|
||||
&username = read_line()
|
||||
username.len() > 0
|
||||
}
|
||||
println("Hello, " + username)
|
||||
|
||||
There is one more thing here that I would like to change: We assign a default value to `username`, in this case an empty string `""`.
|
||||
Default values like this one can lead to all kinds of unexpected behavior and, in my opinion, should be avoided whenever possible.
|
||||
Luckily, mers' `loop` can actually return something - specifically, it returns the `match` of the inner statement, if there is one.
|
||||
Sounds quite abstract, but is very simple once you understand matching.
|
||||
|
||||
# Matching
|
||||
|
||||
In mers, some values match and some don't.
|
||||
This is often used for advanced conditions, but we also need it to break from a loop.
|
||||
|
||||
- Values that don't match are an empty tuple `[]`, `false`, and enums.
|
||||
- Tuples of length 1 match with the value contained in them: `[4]` becomes `4`.
|
||||
- Other values match and don't change: `true` becomes `true`, `"some text"` becomes `"some text"`.
|
||||
|
||||
# Loops... again
|
||||
|
||||
The actual reason the loop stops once we return `true` is because `true` is a value that matches.
|
||||
We didn't do anything with it, but the loop actually returned `true` in the previous example.
|
||||
Since the value we want to get out of the loop is the `username`, not just `true`, we have to return the username from the loop:
|
||||
|
||||
username := loop {
|
||||
println("Hello! Please enter your name.")
|
||||
input := read_line()
|
||||
if input.len() > 0 {
|
||||
input
|
||||
}
|
||||
}
|
||||
println("Hello, " + username)
|
||||
|
||||
If the input isn't empty, we return it from the loop since a value of type `string` will match. The value is then assigned to `username` and printed.
|
||||
|
||||
If the input was still empty, `input.len() > 0` will return `false`, causing the `if` statement to return `[]`, which doesn't match, so the loop will continue.
|
||||
|
||||
# Match statements
|
||||
|
||||
Match statements let you define multiple conditions in a row.
|
||||
Their superpower is that they use matching, while `if` statements just use `bool`s (`true` and `false`).
|
||||
One of my favorite examples for mers' strength is this:
|
||||
|
||||
input := read_line()
|
||||
number := match {
|
||||
input.parse_int() n n
|
||||
input.parse_float() n n
|
||||
[true] [] Err: "couldn't parse"
|
||||
}
|
||||
number.debug()
|
||||
|
||||
Unfortunately, this needs quite a lengthy explanation.
|
||||
|
||||
First, `parse_int()` and `parse_float()`. These are functions that take a string as their argument and return `[]/int` or `[]/float`.
|
||||
They try to read a number from the text and return it. If this fails, they return `[]`.
|
||||
|
||||
Conveniently, `[]` doesn't match while `int` and `float` values do.
|
||||
|
||||
This is where the magic happens: the `match` statement.
|
||||
Between the `{` and the `}`, you can put as many "match arms" as you want.
|
||||
Each of these arms consists of three statements: the condition, something to assign the value to, and an action.
|
||||
|
||||
Let's look at the match arm `input.parse_int() n n`.
|
||||
The three statements here are `input.parse_int()` (condition), `n` (assign_to), and `n` (action).
|
||||
|
||||
If the input isn't a number, `input.parse_int()` will return `[]`. Since this doesn't match, the second match arm (`input.parse_float()`) will try to parse it to a float instead.
|
||||
|
||||
If the input is a number, `input.parse_int()` will return an `int`. Since this matches, the match arm will be executed.
|
||||
First, the matched value - the `int` - will be assigned to `n`. the assign_to part behaves like the left side of a `:=` expression (it supports destructuring and will declare new variables).
|
||||
Finally, the action statement uses our new variable `n` which contains the number we have parsed and returns it from the match statement.
|
||||
|
||||
Since the two arms in the match statement return `int` and `float`, the match statement will return `int/float/[]`.
|
||||
To get rid of the `[]`, we need to add a third arm: `[true] [] Err: "couldn't parse"`. `[true]` is a value that the compiler knows will always match - a tuple of length 1. Assigning something to an empty tuple `[]` just gets rid of the value.
|
||||
The return type is now `int/float/Err(string)`.
|
||||
|
||||
Finally, we `debug()` the variable. Debug is a builtin function that prints the expected type (statically determined at compile-time), the actual type, and the value.
|
||||
If we input `12.3`, it outputs `int/float/Err(string) :: float :: 12.3`.
|
||||
If we input `9`, it outputs `int/float/Err(string) :: int :: 9`.
|
||||
|
||||
# Switch statements
|
||||
|
||||
As demonstrated in the previous example, variables (and all values) in mers can have a type that actually represents a list of possible types.
|
||||
If you wish to filter the types, you can use the `switch` statement.
|
||||
|
||||
For example, here we only print "Number: _" if x is actually a number.
|
||||
|
||||
x := if true 10 else "text"
|
||||
switch x {
|
||||
int num println("Number: " + num.to_string())
|
||||
}
|
||||
|
||||
In most cases, you should use `switch!` instead of `switch`.
|
||||
This simply forces you to handle all possible types `x` could have:
|
||||
|
||||
x := if true 10 else "text"
|
||||
switch! x {
|
||||
int num println("Number: " + num.to_string())
|
||||
}
|
||||
|
||||
After adding the `!`, mers will refuse to compile:
|
||||
|
||||
> Switch! statement, but not all types covered. Types to cover: string
|
||||
|
||||
To fix this, we have to cover the `string` type:
|
||||
|
||||
|
||||
x := if true 10 else "text"
|
||||
switch! x {
|
||||
int num println("Number: " + num.to_string())
|
||||
string s println("String: " + s)
|
||||
}
|
||||
|
||||
We have now covered every possible type `x` can have, and mers happily accepts the `!`.
|
||||
By adding the `!`, mers will not add `[]` to the switch statement's output type, since one of the arms will always be executed and provide some value that eliminates the need for a `[]` fallback.
|
||||
|
||||
# Loops - For
|
||||
|
||||
Mers also has a for loop. The syntax for it is `for <assign_to> <iterator> <what_to_do>`, for example `for number [1, 2, 4, 8, 16, ...] { println("Number: " + number.to_string()) }`.
|
||||
The `...]` indicates that this is a list instead of a tuple. In this case, it doesn't make a difference, but lists are more common in for loops which is why this is what the example uses.
|
||||
|
||||
Just like normal `loop`s, the for loop will exit if `<what_to_do>` returns a value that matches.
|
||||
|
||||
If you want custom iterators, all you need is a function that takes no arguments and returns any value. If the returned value matches, it is assigned and the loop will run. If it doesn't match, the loop will exit.
|
||||
|
||||
# END
|
||||
|
||||
The best way to learn about mers is to use it. If you get stuck, a look at the examples or the syntax cheat sheet may help. Good luck, have fun!
|
||||
155
old/docs/statements.md
Executable file
155
old/docs/statements.md
Executable file
@@ -0,0 +1,155 @@
|
||||
# Mers statements overview
|
||||
|
||||
This is the documentation for mers statements.
|
||||
In code, statements are represented by `SStatement`, `SStatementEnum`, `RStatement`, and `RStatementEnum`.
|
||||
|
||||
## Statement prefixes
|
||||
|
||||
A statement can be prefixed with any number of stars `*`. This is called dereferencing and turns a reference to a value into the value itself. Modifying the value after a dereference leaves the value the reference was pointing to untouched (the data will be cloned to achieve this).
|
||||
|
||||
A statement can also be prefixed with an arrow followed by the type the statement will have: `-> int/float 12`.
|
||||
Although the statement will always return an `int`, mers will assume that it could also return a float.
|
||||
If the statement's output doesn't fit in the forced type (`-> int "text"`), mers will generate an error.
|
||||
In combination with functions, this is similar to rust's syntax:
|
||||
|
||||
fn my_func(a int, b int) -> int {
|
||||
a + b
|
||||
}
|
||||
|
||||
## Assignment
|
||||
|
||||
Statements can assign their value instead of returning it.
|
||||
The syntax for this is `<ref_statement> = <statement>` or `<assign_to> := <statement>`.
|
||||
|
||||
If just `=` is used, the left side must return a reference to some value which will then be changed to the value generated on the right.
|
||||
If `:=` is used, new variables can be declared on the left.
|
||||
|
||||
Destructuring is possible too: `[a, b] := [12, 15]`.
|
||||
|
||||
# Different statements
|
||||
|
||||
## Value
|
||||
|
||||
A statement that always returns a certain value: `10`, `"Hello, World"`, ...
|
||||
|
||||
## Code block
|
||||
|
||||
Code blocks are a single statement, but they can contain any number of statements.
|
||||
They have their own scope, so local variables declared in them aren't accessible from outside.
|
||||
|
||||
They return whatever the last statement they contain returns. Empty code blocks (ones without any statements: `{}`) return `[]`.
|
||||
|
||||
list := [1, 2, 3, 4 ...]
|
||||
sum := {
|
||||
// a temporary variable that only exists in this block
|
||||
counter := 0
|
||||
for elem list {
|
||||
&counter = counter + elem
|
||||
}
|
||||
// the final value will be returned from the block and then assigned to sum.
|
||||
counter
|
||||
}
|
||||
|
||||
## Tuple and list
|
||||
|
||||
These statements contain any number of statements and return the returned values in order as a tuple or list.
|
||||
|
||||
// x is a tuple and has type [int string float]
|
||||
x := [
|
||||
get_num(),
|
||||
get_text(),
|
||||
12.5
|
||||
]
|
||||
// y is a list and has type [int/string/float ...]
|
||||
x := [
|
||||
get_num(),
|
||||
get_text(),
|
||||
12.5
|
||||
...]
|
||||
|
||||
## Variable
|
||||
|
||||
These statements retrieve the value from a variable name:
|
||||
|
||||
x := "my value"
|
||||
println(
|
||||
x // <- this is a variable statement - it gets the "my value" from above so the println function can use it.
|
||||
)
|
||||
|
||||
They can also get references if the variable name is prefixed with the `&` symbol:
|
||||
|
||||
debug(&x) // type is &string
|
||||
|
||||
This is why `&` is required to assign to a variable:
|
||||
|
||||
x = "something else" // can't assign to string!
|
||||
&x = "something else" // only data behind references can be changed - we can assign to &string.
|
||||
|
||||
## Function call
|
||||
|
||||
Function calls give some data to a function and return the result from that function.
|
||||
|
||||
There are 3 kinds of function calls:
|
||||
|
||||
- calls to builtin functions
|
||||
- calls to library functions
|
||||
- calls to custom functions
|
||||
|
||||
Anonymous functions are called using the builtin `run` function.
|
||||
|
||||
## If statement
|
||||
|
||||
Allow for conditional execution of code paths:
|
||||
|
||||
if condition() {
|
||||
// do something
|
||||
}
|
||||
|
||||
if condition() {
|
||||
// do something
|
||||
} else {
|
||||
// do something else
|
||||
}
|
||||
|
||||
## Loop
|
||||
|
||||
Executes code repeatedly:
|
||||
|
||||
counter := 0
|
||||
loop {
|
||||
&counter = counter + 1
|
||||
println("c: " + counter.to_string())
|
||||
}
|
||||
|
||||
## For loop
|
||||
|
||||
## Switch
|
||||
|
||||
## Match
|
||||
|
||||
## Enum
|
||||
|
||||
Wrap a value in an enum: `EnumName: <statement>`
|
||||
|
||||
# dot-chains
|
||||
|
||||
Statements can be followed by the dot `.` character. This has different meanings depending on what comes after the dot:
|
||||
|
||||
## A function call
|
||||
|
||||
In this case, the statement before the dot is prepended to the function arguments:
|
||||
|
||||
a.func(b, c)
|
||||
func(a, b, c)
|
||||
|
||||
## An integer
|
||||
|
||||
Gets the n-th thing from a tuple (uses zero-based indexing):
|
||||
|
||||
[1, "2", 3.0].1 == "2"
|
||||
|
||||
If the value before the dot is a reference to a tuple, a reference to the thing inside the tuple will be obtained:
|
||||
|
||||
x := [1, "2", 3.0]
|
||||
// we assign to the "2", which changes it.
|
||||
&x.1 = "4"
|
||||
141
old/docs/syntax_cheat_sheet.md
Executable file
141
old/docs/syntax_cheat_sheet.md
Executable file
@@ -0,0 +1,141 @@
|
||||
# mers syntax cheat sheet
|
||||
|
||||
## Types
|
||||
|
||||
- `bool`
|
||||
- `int`
|
||||
- `float`
|
||||
- `string`
|
||||
- tuple: `[<type1> <type2> <type3> <...>]`
|
||||
- list: `[<type> ...]`
|
||||
- function: `fn(<input-output-map>)` (might change? depends on implementation of generics)
|
||||
- thread: `thread(<return_type>)`
|
||||
- reference: `&<type>` or `&(<type1>/<type2>/<...>)`
|
||||
- enum: `EnumName(<type>)`
|
||||
- one of multiple types: `<type1>/<type2>/<type3>`
|
||||
|
||||
## Values
|
||||
|
||||
- bool
|
||||
+ `true` or `false`
|
||||
- int
|
||||
+ decimal: `2`, `+2`, `-5`, `0`, ...
|
||||
- float
|
||||
+ decimal: `1.5`, `0.5`, `-5.2`, `2.0`, ... (this is recommended for compatability and clarity)
|
||||
+ whole numbers: `1.0`, `1.`, ... (may break with future updates)
|
||||
+ numbers from 0 to 1: `.5` would be ambiguous following tuples, so is not supported.
|
||||
- string
|
||||
+ `"my string"` (double quotes)
|
||||
+ `"it's called \"<insert name>\""` (escape inner double quotes with backslash)
|
||||
+ all escape sequences
|
||||
* `\"` double quote character
|
||||
* `\\` backslash character
|
||||
* `\n` newline
|
||||
* `\r` carriage return
|
||||
* `\t` tab
|
||||
* `\0` null
|
||||
- tuple
|
||||
+ `[<val1> <val2> <val3> <...>]`
|
||||
- list
|
||||
+ `[<val1> <val2> <val3> <...> ...]` (like tuple, but `...]` instead of `]`)
|
||||
- function
|
||||
+ `(<arg1> <arg2> <arg3> <...>) <statement>` where `<argn>` is `<namen> <typen>`.
|
||||
- thread
|
||||
+ returned by the builtin function `thread()`
|
||||
- reference
|
||||
+ to a variable: `&<varname>`
|
||||
+ to something else: usually using `get()` or its equivalent on a reference to a container instead of the container itself: `&list.get()` instead of `list.get()`
|
||||
- enum
|
||||
+ `<EnumName>: <statement>`
|
||||
|
||||
## Variables
|
||||
|
||||
- declaration and initialization
|
||||
+ `<var_name> := <statement>`
|
||||
+ can shadow previous variables with the same name: `x := 5 { x := 10 debug(x) } debug(x)` prints `10` and then `5`.
|
||||
- assignment
|
||||
+ `&<var_name> = <statement>`
|
||||
* modifies the value: `x := 5 { &x = 10 debug(x) } debug(x)` prints `10` and then `10`.
|
||||
+ `<statement_left> = <statement_right>`
|
||||
* assigns the value returned by `<statement_right>` to the value behind the reference returned by `<statement_left>`.
|
||||
* if `<statement_right>` returns `<type>`, `<statement_left>` has to return `&<type>`.
|
||||
* this is why `&<var_name> = <statement>` is the way it is.
|
||||
+ `***<statement_left> = <statement_right>`
|
||||
* same as before, but performs dereferences: `&&&&int` becomes `&int` (minus 3 references because 3 `*`s), so a value of type `int` can be assigned to it.
|
||||
- destructuring
|
||||
+ values can be destructured into tuples or lists (as of right now):
|
||||
+ `[a, b] := [10, "some text"]` is effectively the same as `a := 10, b := "some text"`
|
||||
|
||||
## Statements
|
||||
|
||||
- value
|
||||
+ `10`, `true`, `"text"`, `"[]"`, etc
|
||||
- tuple
|
||||
+ `[<statement1> <statement2> <...>]`
|
||||
- list
|
||||
+ `[<statement1> <statement2> <...> ...]`
|
||||
- variable
|
||||
+ `<var_name>` (if the name of the variable isn't a value or some other kind of statement)
|
||||
- function call
|
||||
+ `<fn_name>(<arg1> <arg2> <...>)`
|
||||
+ `<fn_name>(<arg1>, <arg2>, <...>)`
|
||||
+ `<arg1>.<fn_name>(<arg2>, <...>)`
|
||||
- function definition
|
||||
+ `fn <fn_name>(<arg1> <arg2> <...>) <statement>` where `<argn>` is `<namen> <typen>`
|
||||
- block
|
||||
+ `{ <statement1> <statement2> <...> }`
|
||||
- if
|
||||
+ `if <condition> <statement>` (`if true println("test")`)
|
||||
+ `if <condition> <statement> else <statement>` (`if false println("test") else println("value was false")`)
|
||||
- loop
|
||||
+ `loop <statement>`
|
||||
+ if the statement returns a value that matches, the loop will end and return the matched value
|
||||
+ the loop's return type is the match of the return type of the statement
|
||||
- for loop
|
||||
+ `for <assign_to> <iterator> <statement>`
|
||||
+ in each iteration, the variable will be initialized with a value from the iterator.
|
||||
+ iterators can be lists, tuples, or functions.
|
||||
+ for function iterators, as long as the returned value matches, the matched value will be used. if the value doesn't match, the loop ends.
|
||||
+ if the statement returns a value that matches, the loop will end and return the matched value
|
||||
+ the loop's return type is the match of the return type of the statement or `[]` (if the loop ends because the iterator ended)
|
||||
- switch
|
||||
+ `switch <value> { <arm1> <arm2> <...> }`
|
||||
* where `<armn>` is `<typen> <assign_to> <statementn>`
|
||||
* if the variable's value is of type `<typen>`, `<statementn>` will be executed with `<value>` assigned to `<assign_to>`.
|
||||
* if the variables type is included multiple times, only the first match will be executed.
|
||||
* within the statement of an arm, the variables type is that specified in `<typen>`, and not its original type (which may be too broad to work with)
|
||||
+ `switch! <var_name> { <arm1> <arm2> <...> }`
|
||||
* same as above, but all types the variable could have must be covered
|
||||
* the additional `[]` in the return type isn't added here since it is impossible not to run the statement of one of the arms.
|
||||
- match
|
||||
+ `match { <arm1> <arm2> <...> }`
|
||||
* where `<armn>` is `<(condition) statement> <assign_to> <(action) statement>`
|
||||
* each arm has a condition statement, something that the matched value will be assigned to, and an action statement.
|
||||
* if the value returned by the condition statement matches, the matched value is assigned to `<assign_to>` and the action statement is executed.
|
||||
* only the first matching arm will be executed. if no arm was executed, `[]` is returned.
|
||||
- fixed-indexing
|
||||
+ `<statement>.n` where `n` is a fixed number (not a variable, just `0`, `1`, `2`, ...)
|
||||
+ `<statement>` must return a tuple or a reference to one. `<statement>.n` then refers to the nth value in that tuple.
|
||||
+ for references to a tuple, references to the inner values are returned.
|
||||
- enum
|
||||
+ `<EnumName>: <statement>`
|
||||
- type definition
|
||||
+ `type <name> <type>` (`type Message [string string]`)
|
||||
- macros
|
||||
+ `!(<macro_type> <...>)`
|
||||
+ `!(mers { <code> })`
|
||||
* compiles and runs the code at compile-time, then returns the computed value at runtime.
|
||||
+ `!(mers <file_path>)` or `!(mers "<file path with spaces and \" double quotes>")`
|
||||
* same as above, but reads code from a file instead
|
||||
* path can be relative
|
||||
|
||||
## Matching
|
||||
|
||||
- values that don't match
|
||||
+ `[]`
|
||||
+ `false`
|
||||
- values that match
|
||||
+ `[v]` -> `v`
|
||||
+ `v` -> `v` unless otherwise specified
|
||||
- invalid
|
||||
+ `[v1 v2]` or any tuple whose length isn't `0` or `1`
|
||||
Reference in New Issue
Block a user