mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +01:00
updated site + added docs/builtins.md + statements can end with ',' to differentiate '1, -2' from '1 - 2' == '-1'.
This commit is contained in:
parent
46653666f0
commit
e766e78e96
@ -32,4 +32,7 @@ Now, create a new text file (or choose one from the examples) and run it: `mers
|
||||
## Docs
|
||||
|
||||
[syntax cheat sheet](docs/syntax_cheat_sheet.md)
|
||||
|
||||
[intro](docs/intro.md)
|
||||
|
||||
[builtins](docs/builtins.md)
|
||||
|
160
docs/builtins.md
Normal file
160
docs/builtins.md
Normal file
@ -0,0 +1,160 @@
|
||||
# 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
|
||||
|
||||
returns a list of matches of the arg0 regex that were found in the string arg1
|
@ -9,7 +9,7 @@
|
||||
<section class="container">
|
||||
<section class="container_left2 code-border">
|
||||
<pre><code class="mers-code-snippet">
|
||||
fn get_number_input(question string) {<br> println(question)<br> input = read_line()<br> // try to parse to an int, then a float.<br> in = match input {<br> input.parse_int() input<br> input.parse_float() input<br> }<br> // 'in' has type int/float/[] because of the match statement<br> switch! in {<br> int/float in<br> // replace [] with an appropriate error before returning<br> [] Err: "input was not a number."<br> }<br> // return type is int/float/Err(string)<br>}<br><br>answer = get_number_input("What is your favorite number?")<br><br>answer.debug() // type: int/float/Err(string)<br>// switch can be used to branch based on a variables type.<br>// switch! indicates that every possible type must be handled.<br>switch! answer {<br> int {<br> println("Entered an integer")<br> answer.debug() // type: int<br> }<br> float {<br> println("Entered a decimal number")<br> answer.debug() // type: float<br> }<br> Err(string) println("Input was not a number!")<br>}<br><br>// wait one second<br>sleep(1)<br><br><br>// function that returns an anonymous function (function object).<br>// anonymous functions can be used as iterators in for-loops.<br>fn square_numbers() {<br> i = 0<br> () {<br> i = i + 1<br> i * i<br> }<br>}<br><br>for num square_numbers() {<br> println(num.to_string())<br> // once num is greater than 60, the loop stops.<br> num.gt(50)<br>}<br></code></pre>
|
||||
fn get_number_input(question string) {<br> println(question)<br> input := read_line()<br> // try to parse to an int, then a float.<br> in := match input {<br> input.parse_int() input<br> input.parse_float() input<br> }<br> // 'in' has type int/float/[] because of the match statement<br> switch! in {<br> int/float in<br> // replace [] with an appropriate error before returning<br> [] Err: "input was not a number."<br> }<br> // return type is int/float/Err(string)<br>}<br><br>answer := get_number_input("What is your favorite number?")<br><br>answer.debug() // type: int/float/Err(string)<br>// switch can be used to branch based on a variables type.<br>// switch! indicates that every possible type must be handled.<br>switch! answer {<br> int {<br> println("Entered an integer")<br> answer.debug() // type: int<br> }<br> float {<br> println("Entered a decimal number")<br> answer.debug() // type: float<br> }<br> Err(string) println("Input was not a number!")<br>}<br><br>// wait one second<br>sleep(1)<br><br><br>// function that returns an anonymous function (function object).<br>// anonymous functions can be used as iterators in for-loops.<br>fn square_numbers() {<br> i := 0<br> () {<br> &i = i + 1<br> i * i<br> }<br>}<br><br>for num square_numbers() {<br> println(num.to_string())<br> // once num is greater than 60, the loop stops.<br> num.gt(50)<br>}<br></code></pre>
|
||||
</section>
|
||||
<section class="container_right">
|
||||
<image
|
||||
@ -42,7 +42,7 @@ it is by using one of the assume*() functions (similar to unwrap()s).
|
||||
<pre class="container2_left"><code>
|
||||
<!DOCTYPE html><br># This document will be processed by build.mers.<br># Lines starting with hashtags are comments and will be ignored.<br># Lines starting with dollar-signs insert special text.<br># To escape this, put a space before the hashtag or dollar sign.<br><head><br> <meta charset=“UTF-8”><br> <link rel="stylesheet" href="external.css"><br> <title>Mark :: mers</title><br></head><br><body><br> <h1>Mers</h1><br> <section class="container"><br> <section class="container_left2 code-border"><br> <pre><code class="mers-code-snippet"><br>$welcome_script<br> </code></pre><br> </section><br> <section class="container_right"><br> <image<br> alt="some picture related to mers (todo)"<br> src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////<br> width="100%" height="100%"<br> ><br> <h3>Mers types</h3><br> <div><br> Mers uses a multiple-types system.<br> It keeps track of which types a variable could have<br> and constructs a type with that information.<br> <br><br> For example, <code>int/float</code> can represent a number - int or<br> Optional types can be <code>[]/[t]</code> - either nothing or one v<br> Mers doesn't have null, it just has the empty tuple <code>[]</code><br> </div><br> <h3>No exceptions, no crashes</h3><br> <div><br> Errors in mers are passed as values.<br> Because of the type system, you are forced to handle them explicitl<br> Mers will not crash in unexpected places, because the only way to c<br> it is by using one of the assume*() functions (similar to unwrap()s<br> </div><br> </section><br> </section><br> <hr><br> <h3>HTML preprocessor to help build this document written in mers:</h3><br> <section class="container"><br> <pre class="container2_left"><code><br>$index.html<br> </code></pre><br> <pre class="container2_right"><code class="mers-code-snippet"><br>$build_script<br> </code></pre><br> </section><br></body><br><br></code></pre>
|
||||
<pre class="container2_right"><code class="mers-code-snippet">
|
||||
#!/usr/bin/env mers<br><br>// helper functions<br><br>fn read_string(path string) {<br> bytes_to_string(fs_read(path).assume_no_enum()).assume_no_enum()<br>}<br>fn code_to_html(code string code_width_limit_chars int) {<br> out = ""<br> for line code.regex(".*").assume_no_enum() {<br> if code_width_limit_chars.gtoe(0).and(line.len().gt(code_width_limit_chars)) {<br> line = line.substring(0 code_width_limit_chars)<br> }<br> line = line<br> .replace("&" "&amp;")<br> .replace("<" "&lt;")<br> .replace(">" "&gt;")<br> out = out.add(line.add("<br>"))<br> }<br> out<br>}<br><br>// data<br><br>index = read_string("index.html")<br><br>index_html = index.code_to_html(75)<br>build_script = read_string("build.mers").code_to_html(-1)<br>welcome_script = read_string("welcome.mers").code_to_html(-1)<br><br>// process index.html<br><br>out = ""<br>for line index.regex("\\S*.*").assume_no_enum() {<br> if line.starts_with("#") {<br> // comment, ignore<br> } else if line.starts_with("$") {<br> if line == "$welcome_script" {<br> out = out + welcome_script<br> } else if line == "$build_script" {<br> out = out + build_script<br> } else if line == "$index.html" {<br> out = out + index_html<br> }<br> } else {<br> // remove spaces<br> loop {<br> if line.starts_with(" ") {<br> line = line.substring(1)<br> } else {<br> true // break<br> }<br> }<br> out = out + line + "\n"<br> }<br>}<br>fs_write("../index.html" string_to_bytes(out)).assume_no_enum()<br><br></code></pre>
|
||||
#!/usr/bin/env mers<br><br>// helper functions<br><br>fn read_string(path string) {<br> bytes_to_string(fs_read(path).assume_no_enum()).assume_no_enum()<br>}<br>fn code_to_html(code string code_width_limit_chars int) {<br> out := ""<br> for line code.regex(".*").assume_no_enum() {<br> if code_width_limit_chars.gtoe(0).and(line.len().gt(code_width_limit_chars)) {<br> &line = line.substring(0 code_width_limit_chars)<br> }<br> &line = line<br> .replace("&" "&amp;")<br> .replace("<" "&lt;")<br> .replace(">" "&gt;")<br> &out = out.add(line.add("<br>"))<br> }<br> out<br>}<br><br>// data<br><br>index := read_string("index.html")<br><br>index_html := index.code_to_html(75)<br>build_script := read_string("build.mers").code_to_html(-1)<br>welcome_script := read_string("welcome.mers").code_to_html(-1)<br><br>// process index.html<br><br>out := ""<br>for line index.regex("\\S*.*").assume_no_enum() {<br> if line.starts_with("#") {<br> // comment, ignore<br> } else if line.starts_with("$") {<br> if line == "$welcome_script" {<br> &out = out + welcome_script<br> } else if line == "$build_script" {<br> &out = out + build_script<br> } else if line == "$index.html" {<br> &out = out + index_html<br> }<br> } else {<br> // remove spaces<br> loop {<br> if line.starts_with(" ") {<br> &line = line.substring(1)<br> } else {<br> true // break<br> }<br> }<br> &out = out + line + "\n"<br> }<br>}<br>fs_write("../index.html" string_to_bytes(out)).assume_no_enum()<br><br></code></pre>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
|
@ -1603,7 +1603,7 @@ impl BuiltinFunction {
|
||||
let left = if left >= 0 {
|
||||
left as usize
|
||||
} else {
|
||||
(a.len() - 1).saturating_sub(left.abs() as _)
|
||||
a.len().saturating_sub(left.abs() as _)
|
||||
};
|
||||
if let Some(len) = len {
|
||||
if len >= 0 {
|
||||
|
@ -280,7 +280,8 @@ impl FormatGs for SStatement {
|
||||
write!(f, " -> ")?;
|
||||
force_opt.fmtgs(f, info, form, file)?;
|
||||
}
|
||||
self.statement.fmtgs(f, info, form, file)
|
||||
self.statement.fmtgs(f, info, form, file)?;
|
||||
write!(f, ",")
|
||||
}
|
||||
}
|
||||
impl Display for SStatement {
|
||||
|
@ -583,7 +583,7 @@ pub mod implementation {
|
||||
let mut start = String::new();
|
||||
loop {
|
||||
fn is_delimeter(ch: char) -> bool {
|
||||
matches!(ch, '}' | ']' | ')' | '.')
|
||||
matches!(ch, '}' | ']' | ')' | '.' | ',')
|
||||
}
|
||||
let nchar = match file.peek() {
|
||||
Some(ch) if is_delimeter(ch) => Some(ch),
|
||||
@ -596,16 +596,18 @@ pub mod implementation {
|
||||
parse_statement(file)?,
|
||||
)));
|
||||
}
|
||||
Some(ch)
|
||||
if (ch.is_whitespace() || is_delimeter(ch)) && start.trim().is_empty() =>
|
||||
{
|
||||
return Err(ParseError {
|
||||
err: ParseErrors::StatementCannotStartWith(ch),
|
||||
location: *file.get_pos(),
|
||||
location_end: None,
|
||||
context: vec![],
|
||||
info: None,
|
||||
});
|
||||
}
|
||||
Some(ch) if ch.is_whitespace() || is_delimeter(ch) => {
|
||||
if start.trim().is_empty() {
|
||||
return Err(ParseError {
|
||||
err: ParseErrors::StatementCannotStartWith(ch),
|
||||
location: *file.get_pos(),
|
||||
location_end: None,
|
||||
context: vec![],
|
||||
info: None,
|
||||
});
|
||||
}
|
||||
file.skip_whitespaces();
|
||||
// parse normal statement
|
||||
let start = start.trim();
|
||||
@ -853,6 +855,10 @@ pub mod implementation {
|
||||
// 080 .
|
||||
// most local (evaluated first)
|
||||
out = match (chain_level, file.peek()) {
|
||||
(_, Some(',')) => {
|
||||
file.next();
|
||||
break;
|
||||
}
|
||||
// 080 .
|
||||
(0..=80, Some('.'))
|
||||
if !matches!(
|
||||
|
@ -6,52 +6,52 @@ fn read_string(path string) {
|
||||
bytes_to_string(fs_read(path).assume_no_enum()).assume_no_enum()
|
||||
}
|
||||
fn code_to_html(code string code_width_limit_chars int) {
|
||||
out = ""
|
||||
out := ""
|
||||
for line code.regex(".*").assume_no_enum() {
|
||||
if code_width_limit_chars.gtoe(0).and(line.len().gt(code_width_limit_chars)) {
|
||||
line = line.substring(0 code_width_limit_chars)
|
||||
&line = line.substring(0 code_width_limit_chars)
|
||||
}
|
||||
line = line
|
||||
&line = line
|
||||
.replace("&" "&")
|
||||
.replace("<" "<")
|
||||
.replace(">" ">")
|
||||
out = out.add(line.add("<br>"))
|
||||
&out = out.add(line.add("<br>"))
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
// data
|
||||
|
||||
index = read_string("index.html")
|
||||
index := read_string("index.html")
|
||||
|
||||
index_html = index.code_to_html(75)
|
||||
build_script = read_string("build.mers").code_to_html(-1)
|
||||
welcome_script = read_string("welcome.mers").code_to_html(-1)
|
||||
index_html := index.code_to_html(75)
|
||||
build_script := read_string("build.mers").code_to_html(-1)
|
||||
welcome_script := read_string("welcome.mers").code_to_html(-1)
|
||||
|
||||
// process index.html
|
||||
|
||||
out = ""
|
||||
out := ""
|
||||
for line index.regex("\\S*.*").assume_no_enum() {
|
||||
if line.starts_with("#") {
|
||||
// comment, ignore
|
||||
} else if line.starts_with("$") {
|
||||
if line == "$welcome_script" {
|
||||
out = out + welcome_script
|
||||
&out = out + welcome_script
|
||||
} else if line == "$build_script" {
|
||||
out = out + build_script
|
||||
&out = out + build_script
|
||||
} else if line == "$index.html" {
|
||||
out = out + index_html
|
||||
&out = out + index_html
|
||||
}
|
||||
} else {
|
||||
// remove spaces
|
||||
loop {
|
||||
if line.starts_with(" ") {
|
||||
line = line.substring(1)
|
||||
&line = line.substring(1)
|
||||
} else {
|
||||
true // break
|
||||
}
|
||||
}
|
||||
out = out + line + "\n"
|
||||
&out = out + line + "\n"
|
||||
}
|
||||
}
|
||||
fs_write("../index.html" string_to_bytes(out)).assume_no_enum()
|
||||
|
@ -1,8 +1,8 @@
|
||||
fn get_number_input(question string) {
|
||||
println(question)
|
||||
input = read_line()
|
||||
input := read_line()
|
||||
// try to parse to an int, then a float.
|
||||
in = match input {
|
||||
in := match input {
|
||||
input.parse_int() input
|
||||
input.parse_float() input
|
||||
}
|
||||
@ -15,7 +15,7 @@ fn get_number_input(question string) {
|
||||
// return type is int/float/Err(string)
|
||||
}
|
||||
|
||||
answer = get_number_input("What is your favorite number?")
|
||||
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.
|
||||
@ -39,9 +39,9 @@ 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 := 0
|
||||
() {
|
||||
i = i + 1
|
||||
&i = i + 1
|
||||
i * i
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user