From 8840890fd11229859b18aa9d515b3657ff051926 Mon Sep 17 00:00:00 2001 From: BeOnDrag Date: Tue, 13 Dec 2022 09:55:47 +0800 Subject: [PATCH] Docs --- README.md | 13 ++++++ docs/CLI.md | 7 +++ docs/Introduction.md | 90 +++++++++++++++++++++++++++++--------- docs/The Built-In Table.md | 85 +++++++++++++++++++++++++++++++++++ interpreter/builtins.py | 64 ++++++++++++++++++++------- 5 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 README.md create mode 100644 docs/CLI.md create mode 100644 docs/The Built-In Table.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..b475dc1 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# The _Miled_ Language + +_Miled_ is an esoteric language. + +Documents are in `docs/` directory. For a quick tutorial, see [docs/Introduction](./docs/Introduction.md). + +## Installation + +**Method 1.** _(Currently only for Windows)_ Download `run-miled.exe` (one-file version) or `run-miled.zip` +(one-folder version) from the Releases page. + +**Method 2.** Clone this repository and run `main.py`. _You should have Python 3.11 or higher installed._ Run +`main.py -h` for help if you use this CLI tool for the first time. diff --git a/docs/CLI.md b/docs/CLI.md new file mode 100644 index 0000000..7645910 --- /dev/null +++ b/docs/CLI.md @@ -0,0 +1,7 @@ +# The `run-miled` CLI Tool + +## Usage + +To run a file, use `-f` or `--file` flag. +To run a code, use `-c` or `--code` flag. +Notice that `-f` takes higher precedence than `-c`. If both present, ignore `-c`. diff --git a/docs/Introduction.md b/docs/Introduction.md index da268c8..e1a53b1 100644 --- a/docs/Introduction.md +++ b/docs/Introduction.md @@ -1,13 +1,6 @@ -# The _Miled_ Language: An Introduction +# The _Miled_ Language -_Miled_ is an esoteric language. The goal of this language is to become as weird as possible. - -I want to make a language that - -1. Uses **prefix notation**, -2. **Curries** all functions, and - -Keeping these basic rules in mind might be helpful to understand some grammar rules. +_Miled_ is an esoteric language. ## Quick Tutorial @@ -19,7 +12,7 @@ Keeping these basic rules in mind might be helpful to understand some grammar ru This is a valid _Miled_ program, and it outputs `Hello, world`, as one would expect. -### Basic rules +### Basic r=Rules 1. Put a space (or any whitespace symbol) between _any two_ lexemes. This is because a word with no spaces will be considered as an identifier, even starting with numbers. So for example, `+ 1 1` is correct, but `+1 1` is not. @@ -28,10 +21,13 @@ which ends at the next quote. 2. **Prefix notation**. The No. 1 rule to remember everywhere. So it's not `2 > 1`, it's `> 2 1`. -3. Functions antomatically consumes input and executes. Imagine calling a function using the normal `f(x, y, ...)` -4. notation, but remove all commas and parentheses; that's how you call functions in _Miled_. +3. Functions automatically consumes input and executes. Imagine calling a function using the normal `f(x, y, ...)` +notation, but remove all commas and parentheses; that's how you call functions in _Miled_. + +4. Since identifiers must be separated by whitespaces, _Miled_ accepts _any_ character in an identifier, unless a quote +appears as the first character. In this case _Miled_ would assume it's the beginning of a string. -### Function calls +### Function Calls **Functions**, or more commonly appeared in the source code as **callers**, are basically automatically curried functions. Take `+` as an example. `+` takes 2 parameters and return the sum of them. Remember that this language @@ -44,14 +40,14 @@ uses _prefix notation_: outputs `3`. To show that it's curried we assign a partially-completed ("_unfulfilled_") caller to a variable: ``` -let x + 1 ;! +:= x + 1 ;! ``` The `;!` at the end means "termination" (explained later), which basically stops `+` from consuming further inputs. This time `x` has yet another parameter to fill in, so we call `x` directly: ``` -let x + 1 ;! x 2 +:= x + 1 ;! x 2 ``` outputs `3`. @@ -64,20 +60,72 @@ with current arguments. After a caller is executed, it doesn't wait for any more input, and will become an immediate value. For example ``` -let x + 1 ;! let y x 2 y +:= x + 1 ;! := y x 2 y ``` -still outputs `3`. (Note: `let` consumes only 2 parameters, so after `let y x 2`, the `let` caller is already fulfilled, +still outputs `3`. (Note: `:=` consumes only 2 parameters, so after `:= y x 2`, the `:=` caller is already fulfilled, and the remaining `y` is returned.) An example with indeterminate-arity callers: ``` -let x n+ ;! let x ;! x 1 ;! x 2 ; +:= x n+ ;! := x ;! x 1 ;! x 2 ; ``` `n+` is the indeterminate-arity version of `+`, which means it will consume all inputs until it's enclosed by `;` (at -the end of this program). Also notice the first `;!` in `let x ;! x 1 ;!`, this is required, as if it's not present, -the interpreter will first try to evaluate the caller `x` and then assign value to the result. +the end of this program). Also notice the first `;!` in `:= x ;! x 1 ;!`, this is required, as if it's not present, +the interpreter will first try to evaluate the caller `x` first. + +For a complete list of builtin callers, refer to [this document](./The Built-In Table.md). + +***NOTICE THAT***, this language is somewhat **stack-based**, so once your parameter is pushed into the stack it cannot +be changed. Take this example: + +``` +:= x [ 1 2 3 ; push x pop x x +``` + +This code defines a list `x = [1, 2, 3]`, then push the popped value. But actually it returns `[1, 2, 3, 3]`! That's +because once `x = [1, 2, 3]` is in the stack of `push`, it is not changed since then. The right way is + +``` +:= x [ 1 2 3 ; pushto pop x x x +``` + +First pop, then push to stack. This returns `[1, 2, 3]`. + +### Control Flow -### Control Flow (WIP) +``` +if: ... :fi +if:: ... else ... :fi +while: ... :ihw +``` + +Nothing much to explain. Just be careful that `else` also closes all open branches (`if:`'s, `while:`'s) after the +nearest `if::`, and `:ihw` closes all open branches after the nearest `while:`. For example: + +``` +while: ... if: ... :ihw +``` + +is valid code. + +### Caller Definition + +``` +def <: ... :> +``` + +Still, nothing much to explain. Example: + +``` +def gcd /S "xy" <: + if:: div min x y max x y min x y + else gcd min x y % max x y min x y :fi +:> +def lgcd "x" <: + while: >= len x 2 pushto gcd pop x pop x x :ihw + @ x 0 +:> +``` diff --git a/docs/The Built-In Table.md b/docs/The Built-In Table.md new file mode 100644 index 0000000..23cc430 --- /dev/null +++ b/docs/The Built-In Table.md @@ -0,0 +1,85 @@ +# The Built-In Table + +Some callers are for control flows, and are thus not presented here. + +## Essentials + +| Caller | Usage | Function | +|:------:|:-------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------| +| `<-` | `<- var value` | Assign value to variable. **Notice that** this also works for variables not in the current scope. | +| `:=` | `:= var value` | Assign value to variable. **Notice that** this works in the current scope; if the variable is not present then creates one. | +| `~` | `~ stmt stmt ... ;` | Continuation. Only returns the last output. | +| `o>` | `o> val` | Output the value. | +| `def` | `def id list-of-param-id <: stmts :>` | Define a caller. | +| `@` | `@ list num` | List access. | +| `@<-` | `@<- list num val` | Change the value at index. | + +### Operators + +| Caller | Usage | Function | +|:-----------------------------------------:|:--------------:|:--------------------------| +| `+`, `-`, `*`, `/`, `//`, `**` | `+ val val` | Same as Python operators. | +| `+=`, `-=`, `*=`, `/=`, `//=` `**=` | `+= var value` | Calculate then assign. | +| `n+`, `n*` | | `n`-ary forms. | +| `0-`, `0-=` | | Unary minus. | +| `-!`, `-=!` | | Absolute difference. | +| `>`, `<`, `>=`, `<=`, `==`, `!=` | | Comparisons | +| `&`, `&=`, `!`, `=!`, `|`, `|=` | | Boolean operations. | + +### Type Conversions + +They're the same as Python's builtin functions. + +| Caller | Function | +|:-------:|:---------------------------| +| `->str` | Convert to string | +| `->int` | Convert to int | +| `->ord` | Convert ASCII to int | +| `->chr` | Convert to ASCII | +| `->cpx` | Convert to complex | +| `->dec` | Convert to decimal (float) | +| `->lst` | Convert to list | + +## More Things + +### List and String Manipulation + +| Caller | Usage | Function | +|:-------------:|:------------------:|:-----------------------------------------------------------------------------------------------------------| +| `rev` | `rev list` | Reverse (same as Python `.reverse()`) | +| `sort` | `sort list` | Sort (same as Python `.sort()`) | +| `lst` | `lst list` | Last element (same as Python `[-1]`) | +| `fst` | `fst list` | First element (same as Python `[0]`) | +| `len` | `len list` | Length (same as Python `len()`) | +| `push` | `push list val` | Push (same as Python `.append()`) | +| `pushto` | `pushto val list` | Push (same as Python `.append()`) | +| `pop` | `pop list` | Pop and return (same as Python `.pop()`) | +| `..=` | `..= num num` | Range, inclusive. | +| `..<` | `..< num num` | Range, exclude the ending index. | +| `:.<` | `:.< num num num` | Range, exclude the ending index, with step. | +| `@?` | `@? list val` | First index of `val` in `list`. | +| `[:]`, `[::]` | `[:] list num num` | Same as Python slices. | +| `+@` | `+@ list num val` | Append `val` before index `num` in `list`. | +| `/S` | `/S str` | Split (same as Python `.split("")` | +| `wS` | `wS str` | Split by whitespace (same as Python `.split()`) | +| `,S` | `,S str` | Split by comma (same as Python `.split(",")`) | +| `join` | `join str list` | Join by string (same as Python `.join()` | +| `map` | `map fn list` | Map `fn` over the list and return the new list.
**Notice that** `fn` should at least take 1 parameter. | + +### Mathematics + +For callers with note `(n)`, adding `n` to it forms its `n`-ary form. +For callers with note `(l)`, adding `l` to it forms its list form. + +| Caller / Variable | Usage | Function | +|:---------------------------------:|:---------:|:------------------------------------------------------| +| `(nl)max`, `(nl)min` | `max x y` | Maximum and minimum (same as Python `max()`, `min()`) | +| `abs`, `sqrt`, `sin`, `cos`, `ln` | | You know what these mean, right? | +| `PI`, `EE`, `II` | | Same as Python `math.pi`, `math.e`, `1j` | + +#### Number-Theoretic + +| Caller / Variable | Usage | Function | +|:-----------------:|:-----------------:|:-------------------------| +| `div` | `div small large` | Divides. Returns `bool`. | + diff --git a/interpreter/builtins.py b/interpreter/builtins.py index c423915..65f86fa 100644 --- a/interpreter/builtins.py +++ b/interpreter/builtins.py @@ -1,19 +1,29 @@ import random from copy import deepcopy as copy +from math import * + from interpreter.util_classes import Caller, Env, Token + + def product(x): p = 1 for a in x: p *= a return p + + def sort(x): x = list(x) x.sort() return x + + def pop(x, t): c = x[0][1][-1] t.set(x[0][0], x[0][1][:-1]) return c, None + + def def_new_caller(param_list: list[str], code: list[Token]): call_id = str(random.randint(0, 65535)) suffix = "$call_arg$" + call_id @@ -35,11 +45,15 @@ def run_new_caller(param: list, old_table: Env): len(param_list), run_new_caller ) + + def map_over(f: Caller, old_list: list, table: Env): return [ copy(f).add_args([(None, i)]).enclose().resolve(table)[0] for i in old_list ] + + BUILTINS_TABLE = Env(table={ "<-": Caller(2, lambda x, t: (t.s(x[0][0], x[1][1]), None)), ":=": Caller(2, lambda x, t: (t.d(x[0][0], x[1][1]), None)), @@ -63,28 +77,25 @@ def map_over(f: Caller, old_list: list, table: Env): "+": Caller(2, lambda x, _: (x[0][1] + x[1][1], None)), "+=": Caller(2, lambda x, t: (t.s(x[0][0], x[0][1] + x[1][1]), None)), - "n+": Caller(-1, lambda x, _: (sum([e[1] for e in x]), None)), + "n+": Caller(-1, lambda x, _: (sum([el[1] for el in x]), None)), "*": Caller(2, lambda x, _: (x[0][1] * x[1][1], None)), "*=": Caller(2, lambda x, t: (t.s(x[0][0], x[0][1] * x[1][1]), None)), - "n*": Caller(-1, lambda x, _: (product([e[1] for e in x]), None)), + "**": Caller(2, lambda x, _: (x[0][1] ** x[1][1], None)), + "**=": Caller(2, lambda x, t: (t.s(x[0][0], x[0][1] ** x[1][1]), None)), + "n*": Caller(-1, lambda x, _: (product([el[1] for el in x]), None)), "-": Caller(2, lambda x, _: (x[0][1] - x[1][1], None)), - "--": Caller(1, lambda x, _: (-x[0][1], None)), + "0-": Caller(1, lambda x, _: (-x[0][1], None)), "-=": Caller(2, lambda x, t: (t.s(x[0][0], x[0][1] - x[1][1]), None)), - "--=": Caller(2, lambda x, t: (t.s(x[0][0], -x[0][1]), None)), + "0-=": Caller(1, lambda x, t: (t.s(x[0][0], -x[0][1]), None)), "-!": Caller(2, lambda x, _: (abs(x[0][1] - x[1][1]), None)), "-=!": Caller(2, lambda x, t: (t.s(x[0][0], abs(x[0][1] - x[1][1])), None)), "/": Caller(2, lambda x, _: (x[0][1] / x[1][1], None)), "/=": Caller(2, lambda x, t: (t.s(x[0][0], x[0][1] / x[1][1]), None)), + "//": Caller(2, lambda x, _: (x[0][1] // x[1][1], None)), + "//=": Caller(2, lambda x, t: (t.s(x[0][0], x[0][1] // x[1][1]), None)), "%": Caller(2, lambda x, _: (x[0][1] % x[1][1], None)), "%=": Caller(2, lambda x, t: (t.s(x[0][0], x[0][1] % x[1][1]), None)), - "max": Caller(2, lambda x, _: (max(x[0][1], x[1][1]), None)), - "min": Caller(2, lambda x, _: (min(x[0][1], x[1][1]), None)), - "nmax": Caller(-1, lambda x, _: (max([e[1] for e in x]), None)), - "nmin": Caller(2, lambda x, _: (min([e[1] for e in x]), None)), - "lmax": Caller(1, lambda x, _: (max(x[0][1]), None)), - "lmin": Caller(1, lambda x, _: (min(x[0][1]), None)), - "if:": Caller(1, lambda x, _: (None, None if x[0][1] else 0)), "if::": Caller(1, lambda x, _: (None, None if x[0][1] else 0)), "else": Caller(0, lambda x, _: (None, 0)), @@ -100,7 +111,7 @@ def map_over(f: Caller, old_list: list, table: Env): "->dec": Caller(1, lambda x, _: (float(x[0][1]), None)), "->lst": Caller(1, lambda x, _: (list(x[0][1]), None)), - "[": Caller(-1, lambda x, _: ([e[1] for e in x], None)), + "[": Caller(-1, lambda x, _: ([el[1] for el in x], None)), "@": Caller(2, lambda x, _: (x[0][1][x[1][1]], None)), "@<-": Caller(3, lambda x, t: (t.s(x[0][0], x[0][1][:x[1][1]] + x[2][1] + x[0][1][x[1][1] + 1:]), None)), @@ -108,15 +119,22 @@ def map_over(f: Caller, old_list: list, table: Env): "rev": Caller(1, lambda x, _: (x[::-1], None)), "sort": Caller(1, lambda x, _: (sort(x[0][1]), None)), - "rep": Caller(1, lambda x, _: (x[0][1] * x[1][1], None)), "lst": Caller(1, lambda x, _: (x[0][1][-1], None)), "fst": Caller(1, lambda x, _: (x[0][1][0], None)), "len": Caller(1, lambda x, _: (len(x[0][1]), None)), "push": Caller(2, lambda x, t: (t.s(x[0][0], x[0][1] + [x[1][1]]), None)), "pushto": Caller(2, lambda x, t: (t.s(x[1][0], x[1][1] + [x[0][1]]), None)), "pop": Caller(1, pop), + "@?": Caller(2, lambda x, _: (x[0][1].index(x[1][1]), None)), + "[:]": Caller(3, lambda x, _: (x[0][1][x[1][1]:x[2][1]], None)), + "[::]": Caller(4, lambda x, _: (x[0][1][x[1][1]:x[2][1]:x[3][1]], None)), + "+@": Caller(3, lambda x, t: (t.s(x[0][0], x[0][1][:x[1][1]] + [x[2][1]] + x[0][1][x[1][1]:]), None)), - "/S": Caller(1, lambda x, _: (list(x[0][1]), None)), + "..<": Caller(2, lambda x, t: (list(range(x[0][1], x[1][1])), None)), + "..=": Caller(2, lambda x, t: (list(range(x[0][1], x[1][1] + 1)), None)), + ":.<": Caller(3, lambda x, t: (list(range(x[0][1], x[1][1], x[2][1])), None)), + + "/S": Caller(1, lambda x, _: (x[0][1].split(""), None)), "wS": Caller(1, lambda x, _: (x[0][1].split(), None)), ",S": Caller(1, lambda x, _: (x[0][1].split(","), None)), @@ -124,6 +142,22 @@ def map_over(f: Caller, old_list: list, table: Env): "map": Caller(2, lambda x, t: (map_over(x[0][1], x[1][1], t), None)), - # * ARITHMETIC FUNCTIONS + # * MORE FUNCTIONS + "max": Caller(2, lambda x, _: (max(x[0][1], x[1][1]), None)), + "min": Caller(2, lambda x, _: (min(x[0][1], x[1][1]), None)), + "nmax": Caller(-1, lambda x, _: (max([el[1] for el in x]), None)), + "nmin": Caller(2, lambda x, _: (min([el[1] for el in x]), None)), + "lmax": Caller(1, lambda x, _: (max(x[0][1]), None)), + "lmin": Caller(1, lambda x, _: (min(x[0][1]), None)), + "abs": Caller(1, lambda x, _: (abs(x[0][1]), None)), + "sqrt": Caller(1, lambda x, _: (sqrt(x[0][1]), None)), + "sin": Caller(1, lambda x, _: (sin(x[0][1]), None)), + "cos": Caller(1, lambda x, _: (cos(x[0][1]), None)), + "ln": Caller(1, lambda x, _: (log(x[0][1], e), None)), + "PI": pi, + "EE": e, + "II": 1j, + + # * Number-Theoretic "div": Caller(2, lambda x, _: (x[1][1] % x[0][1] == 0, None)), })