From 8c6f8c17f1fa41581ccad44df280ae719789cb96 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 4 Jun 2023 21:59:38 +0200 Subject: [PATCH] update readme, docs, site, and version to 0.2.0. --- README.md | 4 +++- docs/intro.md | 22 ++++++++++++---------- docs/statements.md | 12 +++++++++++- index.html | 2 +- mers/Cargo.toml | 2 +- site/welcome.mers | 1 - 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 799cd9c..4a44c4e 100755 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ mers has... + many strings: `[string ...]` (a list) + Either a string or nothing (Rust's `Option`): `string/[]` + Either an int or an error: (Rust's `Result`): `int/string` (better: `int/Err(string)`) -- compile-time execution through (explicit) macro syntax +- compile-time execution through (explicit) macro syntax: `!(mers {})` or `!(mers "file")` ## How mers? @@ -36,3 +36,5 @@ Now, create a new text file (or choose one from the examples) and run it: `mers [intro](docs/intro.md) [builtins](docs/builtins.md) + +[statements](docs/statements.md) diff --git a/docs/intro.md b/docs/intro.md index 7185731..72d7f28 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -14,10 +14,10 @@ To write a comment that spans multiple lines or ends before the end of the line, if 10 != 15 /* pretend this actually meant something... */ { // does something } else { - // TODO: implement this! + // do something else } -To declare a new variable, `:=` is used. +To declare a new variable, use `:=`. println("Hello! Please enter your name.") username := read_line() @@ -79,7 +79,7 @@ By adding `else ` after an if statement, we can define what should h 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 listen to us. +For this, we'll do what any not-insane person would do: Ask them repeatedly until they answer. username := "" loop { @@ -136,6 +136,8 @@ Since the value we want to get out of the loop is the `username`, not just `true 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. @@ -146,7 +148,7 @@ One of my favorite examples for mers' strength is this: number := match { input.parse_int() n n input.parse_float() n n - [true] [] [] + [true] [] Err: "couldn't parse" } number.debug() @@ -155,7 +157,7 @@ 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 (except not - this is obviously on purpose), `[]` doesn't match while `int` and `float` values do. +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. @@ -167,16 +169,16 @@ The three statements here are `input.parse_int()` (condition), `n` (assign_to), 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, with the matched `int` in the right. +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] [] "default value"`. `[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/string`. +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/[] :: float :: 12.3`. -If we input `9`, it outputs `int/float/[] :: int :: 9`. +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 diff --git a/docs/statements.md b/docs/statements.md index aa582e7..775b519 100644 --- a/docs/statements.md +++ b/docs/statements.md @@ -3,7 +3,7 @@ This is the documentation for mers statements. In code, statements are represented by `SStatement`, `SStatementEnum`, `RStatement`, and `RStatementEnum`. -## statement prefixes +## 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). @@ -16,6 +16,16 @@ In combination with functions, this is similar to rust's syntax: a + b } +## Assignment + +Statements can assign their value instead of returning it. +The syntax for this is ` = ` or ` := `. + +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 diff --git a/index.html b/index.html index e470ff2..aac53c8 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@

-fn get_number_input(question string) {
println(question)
input := read_line()
// try to parse to an int, then a float.
in := match {
input.parse_int() n n
input.parse_float() n n
}
// 'in' has type int/float/[] because of the match statement
switch! in {
int/float in in
// replace [] with an appropriate error before returning
[] [] Err: "input was not a number."
}
// return type is int/float/Err(string)
}

answer := get_number_input("What is your favorite number?")

answer.debug() // type: int/float/Err(string)
// switch can be used to branch based on a variables type.
// switch! indicates that every possible type must be handled.
switch! answer {
int num {
println("Entered an integer")
num.debug() // type: int
}
float num {
println("Entered a decimal number")
num.debug() // type: float
}
Err(string) [] println("Input was not a number!")
}

// wait one second
sleep(1)


// function that returns an anonymous function (function object).
// anonymous functions can be used as iterators in for-loops.
fn square_numbers() {
i := 0
() {
&i = i + 1
i * i
}
}

for num square_numbers() {
println(num.to_string())
// once num is greater than 60, the loop stops.
num.gt(50)
}
+fn get_number_input(question string) {
println(question)
input := read_line()
// try to parse to an int, then a float.
in := match {
input.parse_int() n n
input.parse_float() n n
}
// 'in' has type int/float/[] because of the match statement
switch! in {
int/float in in
// replace [] with an appropriate error before returning
[] [] Err: "input was not a number."
}
// return type is int/float/Err(string)
}

answer := get_number_input("What is your favorite number?")

answer.debug() // type: int/float/Err(string)
// switch can be used to branch based on a variables type.
// switch! indicates that every possible type must be handled.
switch! answer {
int num {
println("Entered an integer")
num.debug() // type: int
}
float num {
println("Entered a decimal number")
num.debug() // type: float
}
Err(string) [] println("Input was not a number!")
}

// wait one second
sleep(1)

// function that returns an anonymous function (function object).
// anonymous functions can be used as iterators in for-loops.
fn square_numbers() {
i := 0
() {
&i = i + 1
i * i
}
}

for num square_numbers() {
println(num.to_string())
// once num is greater than 60, the loop stops.
num.gt(50)
}