Skip to content

Commit

Permalink
Docs
Browse files Browse the repository at this point in the history
  • Loading branch information
BeOnDrag committed Dec 13, 2022
1 parent dd791b1 commit 8840890
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 36 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 7 additions & 0 deletions docs/CLI.md
Original file line number Diff line number Diff line change
@@ -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`.
90 changes: 69 additions & 21 deletions docs/Introduction.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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`.
Expand All @@ -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 <name of caller> <list of param> <: ... :>
```

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
:>
```
85 changes: 85 additions & 0 deletions docs/The Built-In Table.md
Original file line number Diff line number Diff line change
@@ -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 |
| `&`, `&=`, `!`, `=!`, `&vert;`, `&vert;=` | | 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.<br/>**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`. |

64 changes: 49 additions & 15 deletions interpreter/builtins.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)),
Expand All @@ -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)),
Expand All @@ -100,30 +111,53 @@ 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)),

# * LIST & STRING MANIPULATION

"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)),

"join": Caller(2, lambda x, _: (x[0][1].join(x[1][1]), None)),

"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)),
})

0 comments on commit 8840890

Please sign in to comment.