From 905265f811415fba222ad134853571890e12ab7d Mon Sep 17 00:00:00 2001 From: William <41281398+WilliamRagstad@users.noreply.github.com> Date: Sun, 25 Oct 2026 14:32:07 +0100 Subject: [PATCH 1/2] Align the reference and control-flow tutorial with verified educational v1 --- .github/workflows/examples.yml | 23 + _posts/2022-03-28-0-intro.md | 6 +- _posts/2022-04-20-1-theory.md | 4 + blog.md | 4 +- documentation.md | 931 +++++---------------------------- index.md | 12 +- tutorial-v1.md | 143 +++++ 7 files changed, 325 insertions(+), 798 deletions(-) create mode 100644 .github/workflows/examples.yml create mode 100644 tutorial-v1.md diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 0000000..569368f --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,23 @@ +name: Executable tutorial examples +on: [push, pull_request] +permissions: + contents: read +jobs: + examples: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + with: + path: tutorial + - uses: actions/checkout@v4 + with: + repository: mini-lang/mini-lang + path: interpreter + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - run: python -m unittest tests.test_documentation -v + working-directory: interpreter + env: + MINI_TUTORIAL_DIR: ../tutorial diff --git a/_posts/2022-03-28-0-intro.md b/_posts/2022-03-28-0-intro.md index f32b5d2..20d39b2 100644 --- a/_posts/2022-03-28-0-intro.md +++ b/_posts/2022-03-28-0-intro.md @@ -18,6 +18,10 @@ permalink: /blog/:title # Introduction +> Current implementation: see the [educational v1 walkthrough](/tutorial/v1) +> and [verified reference](/documentation). The interpreter uses the handwritten +> parser; the Lark grammar is a future design draft. + In this short series, together we will implement a programming language in just one week. This is a challenge for all developers out there who are interested in developing their skills and gaining a deeper understanding of the general principles on which programming languages are based. > I truly believe that every programmer should at least have tried to write their own little toy language in their career, if not just because it's an incredibly creative and fun experience! @@ -55,7 +59,7 @@ Of course, many of you will have constructive feedback, tips and ideas that I wi ## Source code All code and examples can be found on the official mini-lang GitHub repository. -[https://github.com/WilliamRagstad/mini-lang](https://github.com/WilliamRagstad/mini-lang) +[https://github.com/mini-lang/mini-lang](https://github.com/mini-lang/mini-lang) ## Share your experience! Sharing knowledge is one of the greatest abilities of mankind. diff --git a/_posts/2022-04-20-1-theory.md b/_posts/2022-04-20-1-theory.md index f3454a5..d0f3b2b 100644 --- a/_posts/2022-04-20-1-theory.md +++ b/_posts/2022-04-20-1-theory.md @@ -19,6 +19,10 @@ permalink: /blog/:title # The Theory +> Current implementation: see the [educational v1 walkthrough](/tutorial/v1) +> and [verified reference](/documentation). The interpreter uses the handwritten +> parser; the Lark grammar is a future design draft. + > Warning! This article is still under construction! To fully understand something, one must ask *why* until the most fundamental concepts unfolds. diff --git a/blog.md b/blog.md index 3848e94..1a4e4cc 100644 --- a/blog.md +++ b/blog.md @@ -9,7 +9,9 @@ permalink: /blog Welcome to the official [mini-lang](https://www.mini-lang.org) blog! This is a place where you can read about mini-lang, learn about the language, and the community around it. -Below is a list of the most recent posts. +For the current implementation, read [From syntax to working control flow](/tutorial/v1). +Its examples are checked against the educational v1 interpreter. The original +chapters below provide background rather than a complete language specification. ## Recent posts diff --git a/documentation.md b/documentation.md index 8be92c5..64f7013 100644 --- a/documentation.md +++ b/documentation.md @@ -4,821 +4,168 @@ title: Documentation permalink: /documentation --- -# Documentation for the `mini` language - -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) - -Welcome to the documentation for the `mini` language! This is the place to go if you want to learn more about the language and how to use it. The documentation mainly focus on the language [specification](#specification). - -`mini` is a minimal programming language that is built for the purpose of teaching programming language design. -The language is dynamically typed and expression-based. It is designed to be easy to learn and use. There are support for both functional and object oriented programming paradigms. - - - -## Table of contents - -- [Table of contents](#table-of-contents) -- [Features](#features) -- [Specification](#specification) - * [Primitive data types](#primitive-data-types) - * [Variable declaration](#variable-declaration) - * [Comments](#comments) - * [Lambda expressions](#lambda-expressions) - * [Functions](#functions) - * [Collection data structures](#collection-data-structures) - + [More on collection data structures](#more-on-collection-data-structures) - * [Enums](#enums) - * [Classes](#classes) - * [Built-in functions](#built-in-functions) - * [Control flow structures](#control-flow-structures) - * [Other keywords and operators](#other-keywords-and-operators) -- [Contribute! 🎉](#contribute-) - 1. [Perfecting the language 👑](#1-perfecting-the-language-) - 2. [Improve the documentation 📝](#2-improve-the-documentation-) - 3. [Expand the ecosystem 🏕️](#3-expand-the-ecosystem-%EF%B8%8F) - 4. [Get involved in the community 🫂](#4-get-involved-in-the-community-) - 5. [Improve the article series 📚](#5-improve-the-article-series-) -- [License](#license) - -Table of contents generated with markdown-toc - - -
- -## Features - -The language has a rich set of features, including: - -* Basic arithmetic, logic, and comparison operators -* Primitive data types such as `number`, `boolean`, and `string` -* Collection data structures like `list`, `tuple`, `map`, and `set` -* Lambda expressions and functions -* Classes and inheritance -* Enum types -* Variables -* Code blocks -* C-stlye Comments -* A set of built-in functions -* Basic control flow structures -* Other keywords and operators - -## Specification - -The syntax of `mini` is similar to the [ECMAScript specification](https://tc39.es/ecma262/), but not identical. - -### Primitive data types - -There are three primitive data types in `mini`: -* `number` - Represents both floating point and integer numbers - * Arithmetic operators: `+`, `-`, `*`, `/`, `%` (modulo) - * Comparison operators: `==`, `!=`, `<`, `<=`, `>`, `>=` - * Examples: `21`, `4.2`, `-3`, `-3.5` -* `boolean` - Represents logical `true` or `false` - * Logical operators: `&` (and), `|` (or), `!` (not) - * Comparison operators: `==` - * Examples: `true`, `false` -* `string` - Represents a sequence of characters, or a single character - * Arithmetic operators: `+` (concatenation) - * Comparison operators: `==`, `!=` - * Examples: `"hello"`, `'world'`, `"a"`, `'b'` - - -### Variable declaration -A variable declaration is the same as an assignment, but if the variable is not already declared, it is automatically declared. - -```js -x = 10 -``` +# Educational v1 reference -Multiple variables can be declared with the same value in a single statement. - -```js -x = y = z = 10 -``` +Mini is a small, dynamically typed, expression-based interpreter for learning +language implementation. This page describes the **handwritten interpreter**, +not every feature proposed during the project's development. -Or multiple variables can be declared with different values on a single line, must be separated by whitespace. +Use Python **3.10 or later**. No third-party Python packages are required. -```js -x = 10 y = 20 z = 30 +```sh +git clone https://github.com/mini-lang/mini-lang +cd mini-lang +python mini.py examples/hello_world.m +python mini.py repl +python test.py ``` -All variable identifiers **starting** with an underscore `_` are treated as "don't care" variables. Meaning its value is "thrown away" when bound in a scope. +The CLI loads `src/parser.py` and `src/lexer.py`, which re-export `src/manual/`. +`src/evaluator.py` executes the resulting AST. `src/grammar/mini.lark` is an +experimental grammar draft, **not the active parser**. In particular, its colon +bodies, newline rules and pattern syntax do not describe v1. -```ts -_ignored = 10 -print(_ignored) // Error, _ignored is not defined -``` +See the [capability matrix](https://github.com/mini-lang/mini-lang/blob/main/docs/v1.md) +and [implementation tutorial](/tutorial/v1). Every `mini` code fence below runs in +a fresh environment in the interpreter's documentation tests; `// =>` gives the +exact printed output. -This is useful for catching all values in a pattern match. +## Values and expressions -```fsharp -match x - | 10 -> "ten" - | _ -> "other" // Catch all values not already matched -``` +Values include numbers, booleans, strings, lists, tuples, maps and unit `()`. +Operators include `+ - * / % ^`, comparisons, and short-circuit `and` / `or` +(also `&&` / `||`). Negation is `not` or `!`; single `&` and `|` are unsupported. +`(x)` groups an expression; `(x, y)` is a tuple. A one-element tuple literal is +not supported. Unicode letters are allowed in identifiers; emoji belong in strings. -Variables can also be set to the return value of a code block. +Whitespace separates expressions. Newlines are not statement terminators; +semicolons are unsupported. Keep calls and indexes adjacent to their receiver: +`f(x)`, `items[0]`. Comments use `//` or `/* ... */`. -```js -x = { - y = 10 - y * 2 -} -x // 20 +```mini +π = 3.14 +print(π * 2, not false, "hello 🌍") +print(false and missing) +// => 6.28 true hello 🌍 +// => false ``` -### Comments +## Variables, blocks and functions -```ts -// Single-line comment -/* Multi-line - comment */ -``` - -There are also unofficial block comment syntax for documentation. -This is not currently supported in this language, but mainly processed by documentation tools to generate application programming interface (API) documentation. - -```ts -/** - * Documentation comment - * - * @param {number} x - * @param {string} y - * @returns {boolean} - **/ -``` +`=` creates a local name if no enclosing binding exists; otherwise it updates +the nearest existing binding. Identifier `+=` is supported for numbers and +strings. Other compound assignments are deferred. Underscore-prefixed names +are ordinary variables, not discards. -### Lambda expressions - -Lambdas are declared with the `=>` operator. -The left hand side of the `=>` is the parameter-list tuple, and the right hand side is the function body (A single expression or block). - -```js -(x, y) => x + y -``` +Blocks return their last expression, or `()` if empty. New block-local names +do not leak. Functions capture their defining environment, and parameters always +bind locally, shadowing outer names. Functions may use an expression or block body. -Empty parameter-list lambdas can be declared with an empty tuple. Used for generator and supplier functions. - -```js -() => 42 -``` - -### Functions - -A function is simply a lambda expression bound to a variable. -Functions can be declared by assigning a lambda expression to a variable as seen below. - -```cs -add = (x, y) => x + y -add(1, 2) -``` - -Alternative syntax sugar for functions is to directly specify the parameter-list and function body separated by an assignment operator. - -```cs +```mini +x = 10 +{ x += 2 local = 99 } add(x, y) = x + y -``` - -These two forms are equivalent as the latter is a shorthand for the former. - -Functions can also have a body block. - -```cs -add(x, y) = { - z = y * 2 - x + z // Return value -} -``` - -`mini` has therefore **higher-order functions**. -As functions and lambdas are the same thing, they can be used interchangeably. The value of a function is the value of the lambda expression. - -```cs -add = (x, y) => x + y // Lambda expression style -add10(y) = add(10, y) // Function style -[1, 2, 3].map(add10) // Function passed as parameter -// [11, 12, 13] -``` - -### Collection data structures - -Primitive data types are useful, but limiting on their own. `mini` has a number of data structures that can be used to store and manipulate collections of data. -* `list` - An ordered collection of values, where each value can be accessed by index. Elements are stored in the order they are declared. - * Examples: - * `[1, 2, 3]` - A list of three integers - * `[true, false, 'hello', 'world']` - Mixed types - * `[]` - An empty list - * `list[0]` - Access the first element - * `list[-1]` - Access the last element - * `list[5] = 10` - Set the sixth element to 10 - * Operators: - * `list1 + list2` - Concatenate two lists - * `list[index]` - Access an element by index (zero-based) - * Methods: - * `list.length` - The number of values in the list - * `list.append(value)` - Adds a value to the end of the list - * `list.prepend(value)` - Adds a value to the beginning of the list - * `list.pop()` - Removes and returns the last value in the list - * `list.shift()` - Removes and returns the first value in the list - * `list.map(fn)` - Returns a new list with the values transformed by the given function - * `list.filter(fn)` - Returns a new list with the values filtered by the given function - * `list.reduce(fn, initialValue)` - Returns a single value by combining the values in the list - * `list.indexOf(value)` - Returns the index of the given value, or `-1` if the value is not in the list - * `list.lastIndexOf(value)` - Returns the index of the last given value, or `-1` if the value is not in the list - * `list.join(separator)` - Returns a string with the values in the list separated by the given separator -* `tuple` - An ordered collection of values, where each value can be accessed by index. - * Examples: - * `(1, 2, 3)` - A tuple of three integers - * `('hello', 'world')` - A tuple of two strings - * `()` - An empty tuple, or `unit`. **This should be treated the same as `null` in other languages.** - * Operators: - * `tuple1 + tuple2` - Add the values of two tuples together (element-wise). - * `tuple[index]` - The value at the given index - * Methods: - * `tuple.length` - The number of values in the tuple -* `map` - A disorderly collection of hashed key-value pairs, where each key can be accessed by index. - * Examples: - * `#{a: 1, b: 2, c: 3}` - A map of three key-value pairs - * `#{0: 1, 1: 2, 2: 3}` - A map with numeric keys - * `#{'hello world': 'hello', 'goodbye world': 'goodbye'}` - A map with string keys - * `#{}` - An empty map - * `map['test'] = 123` - Assigns a value to the given key - * Operators: - * `map1 + map2` - Concatenate two maps together (merge the values of the two maps). - * `map[key]` - The value at the given key - * Methods: - * `map.` - The value associated with the given key, ie. `map.abc` is equivalent to `map['abc']` - * `map.has(key)` - Returns `true` if the map contains the given key - * `map.delete(key)` - Removes the value associated with the given key - * `map.clear()` - Removes all key-value pairs from the map - * `map.keys()` - Returns a new list with the keys in the map - * `map.values()` - Returns a new list with the values in the map - * `map.entries()` - Returns a new list with the key-value pairs in the map - -#### More on collection data structures - -There are many tips and tricks for working with collections. -For example, it is possible to use a list as a stack. - -```ts -stack = [1, 2, 3] -stack.append(4) -print(stack) // [1, 2, 3, 4] -stack.pop() -print(stack.pop()) // 3 -``` - -It is also possible to use a list as a queue. - -```ts -queue = [1, 2, 3] -queue.append(4) -print(queue) // [1, 2, 3, 4] -queue.shift() -print(queue.shift()) // 2 -``` - -Or even a reversed queue. - -```ts -queue = [3, 2, 1] -queue.prepend(4) -print(queue) // [4, 3, 2, 1] -queue.pop() -print(queue.pop()) // 2 -``` - -Lists have support for partial pattern matching, utilizing the `|` operator to separate elements from the rest of the list in a pattern match. -You can extract values from different collections using pattern matching as shown below. - -```fsharp -match [1, 2, 3] - | [f, s | t] => print(f, s, t) // 1 2 [3] - | [h | t] => print(h) // 1 - | [] => print('empty list') // empty list - | _ => print('other') // other type -``` - -```cs -list = [1, 2, 3] -if list is [head | tail] { - print(head) // 1 - print(tail) // [2, 3] -} -``` - -Maps have also support for partial pattern matching. - -```fsharp -match #{a: 1, b: 2, c: 3} - | #{a: f, b: s} => print(f, s) // 1 2 - | #{a: f} => print(f) // 1 - | #{} => print('empty map') // empty map - | _ => print('other') // other type -``` - -> A **Set** is a collection of unique values.\ -> `mini` provides a `set` data structure in its standard library for storing and manipulating unique values in an unordered collection. - -Examples: -* `set(1, 2, 3)` - Initialize a set with three unique values. -* `set()` - The empty set. - -Methods: -* `set.length` - The number of values in the set -* `set.has(value)` - Returns `true` if the set contains the given value -* `set.add(value)` - Adds the given value to the set -* `set.delete(value)` - Removes the given value from the set -* `set.clear()` - Removes all values from the set -* `set.values()` - Returns a new list with the values in the set - - -### Enums - -`mini` supports Rust-like enums. - -```fsharp -enum Letters = A | B | C -``` - -All identifiers (e.g. `A`, `B` and `C`) in an enum must be **unique**. -This means that the names may not occur more than once in the enum declaration. - -> **Example**: *Invalid enum declaration ❌* -> ```fsharp -> enum Optional = A | B | A -> ``` -> The example above would throw an error, since `A` occurs twice in the enum declaration. - -The enum variant arguments/values are assigned in the order they are declared and are *dynamically typed*. This means that the enum variant arguments/values can be of any type. - -```fsharp -enum Optional = Some(value) | None - -x = Some(42) -y = None -z = Some([1, 2, 3]) -``` - -### Classes - -```ts -Animal = class { - Animal(name) = #{ - name: name - } - - speak() { - return "{this.name} makes a noise." - } -} -``` - -In the example above, `Animal` is a class with a constructor and a `speak` method. -The method is defined as a function expression, which has an implicit `this` parameter. - -`mini` also supports basic inheritance. - -```ts -Dog = class : Animal { - Dog(name) { - // Code block with logic other than just a map - return #{ - name: name, - dogSpecificSound: "Woof" - } - } - - speak() { - return "{this.speak()} {this.dogSpecificSound}!" - } -} -``` - -Running this with the code below will produce the following output: - -```ts -dog = Dog("Fido") -dog.speak() // "Fido makes a noise. Woof!" -``` - -Classes are instantiated simliar to function calls, other languages like Python have a similar syntax. - -```ts -animal = Animal("Pig") -animal.speak() // "Pig makes a noise." -animal.name // Ok, returns "Pig" -animal.dogSpecificSound // Throws an error, since dogSpecificSound is not defined on Animal -``` - -Because classes also are expressions, they can be assigned to variables. This is useful for creating objects that are used in multiple places, or for creating objects that are passed to functions. -This is also how static methods can be defined on a class. - -```ts -MyClass = class { - hello() { - return "Hello world!" - } -} -MyClass.hello() // Throw an error, since hello is only defined on MyClass instances -MyClass.bye() // Throws an error, since bye is not defined on MyClass -myInstance = MyClass() -myInstance.hello() // "Hello world!" -myInstance.bye() // Throws an error, since bye is not defined on MyClass instances -// Define a static method on MyClass -MyClass.bye = () => { - return "Bye world!" -} -MyClass.bye() // "Bye world!" -myInstance.bye() // Throws an error, since bye is not defined on MyClass instances byt on MyClass itself -``` -> Note: `mini` does not support static methods inside of class expressions.\ -> Instead, static methods can be defined on the class value itself as shown above for `MyClass.bye`. - - -### Built-in functions - -There are a number of built-in functions that can be used in `mini`: -* `print` - Prints a string to the console - * Examples: - * `print('hello')` - * `print("hello", "world")` - * `print(1, 2, 3)` -* `input` - Gets a string from the console - * Examples: - * `input()` - * `input("Enter your name: ")` - -There are also a number of helper functions to convert values between different types. -* `string` - String representation of a given value - * Examples: - * `string([1, 2, 3])` - * `string(1.23)` - * `string(true)` - * `string(#{'a': 1, 'b': 2})` - * `string("hello")` -* `number` - Integer representation of a given value - * Examples: - * `number('1')` - * `number(1.23)` - * `number(true)` - Returns `1` - * `number(#{'a': 1, 'b': 2})` - Fails with an error - * `number("hello")` - Fails with an error -* `boolean` - Boolean representation of a given value - * Examples: - * `boolean('1')` - Returns `true` - * `boolean(1.23)` - Returns `true` - * `boolean(0)` - Returns `false` - * `boolean(true)` - Returns `true` - * `boolean("")` - Returns `false` - * `boolean(#{'a': 1, 'b': 2})` - Returns `true` - * `boolean("hello")` - Returns `true` - -### Control flow structures - -There are three control flow structures in `mini`: -* `if` - If-then-else statement -* `match` - Pattern matching statement -* `for` - For-loop -* `while` - While-loop -* `break` - Break out of a loop -* `continue` - Continue to the next iteration of a loop -* `return` - Return from a function, last expression in a block is implicitly returned - -Below are examples of each control flow structure. - -> **`if` statement** -> -> The `if` statement is used to execute and return the value of a block of code or a single expression if a condition guard is true. - -The guard is an expression directly following the `if` keyword, surrounding parentheses are optional. After that, a block of code or a single expression is specified. - -```js -if x == 1 "one" -``` - -The example above returns the string `"one"` if the condition `x == 1` is true. - -```js -if x % 2 == 0 { - x *= 2 - return x + 1 -} -``` - -The example above returns the value of `x * 2 + 1` if `x` is even. - -If statements also has the power to pattern match on values using the `is` operator. - -> **`if-else` statement** -> -> The `if-else` statement is used to execute and return the value of different blocks of code or single expressions depending on the value of a condition guard. - -The code below is a short implementation of the absolute value function `|x|`. - -```js -if x > 0 x -else -x -``` - -> **`else if` statement** -> -> The `else if` statements can be used to add additional conditions to an `if-else` statement. - -```fsharp -if x >= 10 "ten or more" -else if x >= 5 "five or more" -else if x >= 1 "one or more" -else "none" -``` - -> **`match` statement** -> -> The `match` statement matches a value against a set of patterns, and returns the value of the first pattern that matches. - -First specify the value to match against, then specify a list of patterns separated by `|` characters. -The first pattern that matches the value is the one that gets evaluated and returned. - -```fsharp -match x - | 1 => "one" - | 2 => "two" - | _ => "many" -``` - -One could think of match as many `if x is p` statements combined into a single statement. - -A match case can also have a guard using the `if` keyword, which ensures that the pattern on the left hand side also satesfies a logical condition on the right hand side, before evaluating the matching case expression. - -```fsharp -match x - | number(n) if n > 0 => "positive" - | number(n) if n < 0 => "negative" - | number(n) if n == 0 => "zero" - | _ => "not a number" -``` - -You can also inline the pattern list, which is useful when the pattern list is short. - -```fsharp -x = Optional.Some(1) -match x | Some(n) => "some" | None => "none" -``` - -> **`for` statement** -> -> The `for` statement is used to iterate over a list of values or an iterator. - -The syntax is similar to the `for` loop in other languages such as Python and Ruby, using the `in` keyword to specify the list or iterable. - -```fsharp -for x in [1, 2, 3] print(x) -``` - -Using a lazy iterator is possible by using the `..` operator. - -```fsharp -for x in 1..10 print(x) -``` - -It is also possible to iterate over a map where the keys are used as the iterator. This is because the map data structure implements the `Iterator` class interface. - -```ts -m = #{a: 1, 'b': 2, "c": 3} -for x in m - print(x) -// prints a, b, c -``` - -Passing an extra pattern argument, `v` for example, to the `for` statement is used to specify a variable to be used as the iterator value. -In this case the variable `k` is the key and `v` is used to store the value of each map entry. - -```ts -m = #{a: 1, 'b': 2, "c": 3} -for (k, v) in m - print(k, v) -// prints a 1, b 2, c 3 -``` - -> **`while` statement** -> -> The `while` statement is used to execute a block of code while a condition is true. - -The guard is an expression directly following the `while` keyword, surrounding parentheses are optional. After that, a block of code or a single expression is specified. - -```ts -while x < 10 x += 1 -``` - -Alternative styles of writing a while loop are also possible. - -```ts -while x < 10 { - x += 1 -} -``` - -```ts -while (x < 10) x += 1 -``` - -Or both. - -```ts -while (x < 10) { - x += 1 -} -``` - -> **`break` statement** -> -> The `break` statement is used to break out of the nearest enclosing loop. - -```ts -while x < 10 { - x += 1 - if x == 5 break -} -``` - - -> **`continue` statement** -> -> The `continue` statement is used to continue to the next iteration of the nearest enclosing loop. - -```ts -while x < 10 { - x += 1 - if x % 2 == 0 continue - print(x) -} -``` -The example above prints all odd numbers from 1 to 10. E.g. 1, 3, 5, 7, 9. - - -> **`return` statement** -> -> The `return` statement is used to return a value from a function or code block. The last expression in a block is implicitly returned. - -```ts -value = { - x = 1 - if condition return x // Early return if condition is true - y = 2 - x + y // Returned implicitly +square = (x) => x * x +print(x, add(2, 3), square(4)) +// => 12 5 16 +``` + +## Collections and built-ins + +Lists concatenate with `+`; tuples add element-wise; maps merge with `+`. +Indexing supports lists, tuples and maps, including negative list/tuple indexes. +Lists and maps support assignment through computed indexes. Map identifiers are +literal string keys. Integer and boolean literal keys use their string forms. +Slices require explicit start and end; the end is excluded and the step cannot be zero. + +Use free functions such as `list_append`, `list_size`, `list_map`, `list_filter`, +`list_reduce`, `map_size` and `map_contains`. Collection methods such as +`items.append(value)` are not implemented. `print`, `input`, `typeof` and +`assert(condition, message)` are also available. + +```mini +items = [1, 2, 3] +for i in 0..3 { items[i] = items[i] * 2 } +list_append(items, 8) +print(items, items[1:3]) +m = #{name: "Mini"} +key = "name" +m[key] = "Ada" +print(m.name, map_size(m)) +// => [2, 4, 6, 8] [4, 6] +// => Ada 1 +``` + +The conversion functions are `str`, `number`, `int` and `bool`, not `string`. +`number` and `int` return `()` when string conversion fails. The existing `bool` +conversion recognizes the text `"true"` case-insensitively; it is not general +truthiness. Conditions themselves must be booleans. + +```mini +print(str(42), number("1.5"), int("12"), bool("true"), bool(1)) +print(number("invalid")) +// => 42 1.5 12 true false +// => () +``` + +The larger host-oriented standard library is experimental and outside the v1 +example guarantee. + +## Control flow structures + +`if condition { ... } else if condition { ... } else { ... }` returns the chosen +body's value. Without a matching branch or `else`, it returns `()`. +Use braces for clear body boundaries; v1 also retains single-expression bodies +without colons. + +`while condition { ... }` checks a boolean before every iteration. +`for name in values { ... }` evaluates the iterable once and visits list/tuple +elements, string characters or map keys in insertion order. Unit `()` iterates +as an empty tuple. Each iteration has a fresh scope; the for name shadows an +outer name and does not leak. Loops return `()`. + +```mini +n = 0 +while n < 4 { + n += 1 + if n == 2 { continue } + print(n) } -// value can either be 1 (early) or 3 (implicit) +for key in #{a: 1, b: 2} { print(key) } +// => 1 +// => 3 +// => 4 +// => a +// => b ``` +`break` exits the nearest loop; `continue` skips to its next iteration. +Neither may cross a function boundary. `return expression` exits the current +function through nested blocks and loops. Use `return ()` for unit. Return from +a top-level block, and loop control outside a loop body, are errors even when +that code would never run. -### Other keywords and operators - -Other than the operators and keywords listed above, there are a number of other keywords and operators that can be used in `mini`. -* `is` - Used to match values against patterns and bind them to variables -* `in` - Used to check if a value is in a set of values -* `..` - Used to specify a range of values - -> **`is` keyword** -> -> The `is` keyword is used to match values against patterns and bind them to variables. - -Use `is` in an if-else statement to pattern match on values and change the control flow of the program depending on the match result. - -```ts -if x is number "number" -else if x is string "string" -else if x is boolean "boolean" -else "unknown" -``` - -Or more specifically, get the type of the value and bind it to a variable. - -```ts -if x is number(n) "number: " + n -else if x is string(s) "string: " + s -else if x is boolean(b) "boolean: " + b -else "unknown: " + x -``` - -Can be used to handle optional values. - -```fsharp -if x is Optional.Some(v) "some: " + v -else "none" -``` - -Or match on data types. - -```ts -if l is [head | tail] "list: " + head + " " + tail -else -``` - -> **`in` keyword** -> -> The `in` keyword is used to check if a value is in a set of values. - -Check if a value is in a list. - -```ts -if x in [1, 2, 3] "in list" -else "not in list" -``` - -Check if a value is in a range. Read more about the `..` operator below. - -```ts -if x in 1..10 "in range" -else "not in range" -``` - -Check if value is a key in a map. If `x` would be `2` for example, `x` would be in the map and the condition would be true. - -```ts -if x in #{1: "one", 2: "two", 3: "three"} "in map" -else "not in map" -``` +## Ranges -Check if a value is in a tuple. +`start..end` produces an **eager, ascending, end-exclusive integer list**. +Both bounds are required. This is not the older proposed inclusive lazy range. -```ts -if x in (1, 2, 3) "in tuple" -else "not in tuple" +```mini +print(1..4) +print(4..1) +// => [1, 2, 3] +// => [] ``` -One can also use `in` when iterating over a set of values using a `for` loop. - -```ts -for x in [1, 2, 3] print(x) -``` - - -> **`..` Operator** -> -> The `..` operator is used to specify a range of values including the lower and upper bounds. - -```ts -for i in 1..10 { - txt = 'i: ' + i - print(txt) -} -``` - -The example above prints the numbers 1 to 10. - -It is also possible to generate a lazy sequence of values withouth having to store them in memory by excluding the upper bound. - -```ts -for i in 1.. { - if i > 1000 break - txt = 'i: ' + i - print(txt) -} -``` - -## Contribute! 🎉 -Contributions of all kinds are welcome, not only in the form of **code** but also with regards to the official **documentation**, **debugging** help and **tickets/issues** in the bug tracker, support of other users on the **community** forum or the official discord and also **financially**. - -If you think something is bad about the `mini` language or the article series written about it, please **help in any way to make it better** instead of complaining about it. - -For information about how to go about submitting bug reports or pull requests, please see the project's [contribution guidelines](https://github.com/WilliamRagstad/mini-lang/blob/main/CONTRIBUTING.md). - -### 1. Perfecting the language 👑 -There is much more to the language than just the syntax, and there are several ways to improve it. -Optimizing the interpreter and compiler is one of the most important aspects of the tooling for a language. -Do consider contributing to the [mini-lang project](https://github.com/WilliamRagstad/mini-lang) to make your own improvements. - -Do you have your own ideas for improvements? Are they specialized in a certain area or controversial in nature? -Please create your own version of the language by forking it on GitHub and sharing your changes! - -### 2. Improve the documentation 📝 -The documentation is a great source of information for anyone who wants to learn the language. -It is also a great source of information for anyone who wants to contribute to the project. - -If you are a regular user of the language, you can help improve the documentation by creating a pull request to the official [documentation repository](https://github.com/WilliamRagstad/mini-lang). - -### 3. Expand the ecosystem 🏕️ -There are numerous ways of contributing to the `mini` language, one of which is to contribute code to the [official repository](https://github.com/WilliamRagstad/mini-lang), but there are also many other ways to contribute. - -* Consider building projects in `mini` and publish your projects on GitHub. -* Expand on the tools and libraries that are already available for `mini` and publish your own, look at the todo list and the [third-party tools](/TODO.md#Third-party-tools) section. - -### 4. Get involved in the community 🫂 -The best way to show support and help is to get involved in the community! There are many ways to get involved, start by joining the [official discord](https://discord.gg/YzjQZjh) and ask questions, share your ideas and discuss the language there! It is a great place to learn about the language and to get help with any issues you might have, or to even help out with the development of the language. - -### 5. Improve the article series 📚 -If you are a writer and want to contribute to the article series, please see the [contributing to the article series](/CONTRIBUTING#Article-series) section. - -Other ways to contribute to the article series include: -* Commenting on the articles -* Writing articles about the series -* Writing articles about the language and the project +## Errors and deferred features -> ### Citation -> Correctly cite and refer to the `mini` programming language and the article series by using the `🗨️ Cite this repository` button in the right-hand sidebar of this repository. +Lexical, syntax and runtime errors include the filename and one-based line and +column. File execution writes errors to stderr and exits nonzero. The REPL +reports an error and accepts the next input; EOF exits it. `--debug` includes +Python tracebacks for interpreter development. -## License -This project is licensed under the [MIT license](https://github.com/WilliamRagstad/mini-lang/blob/main/LICENSE). -The source code is available on [GitHub](https://github.com/WilliamRagstad/mini-lang). -To modify the code, please fork the project and experiment with the language. -Expanding on the language is highly encouraged. If you have any suggestions, please open an issue or pull request. +Classes, enums, pattern matching (`match`, `is`, destructuring), sets, modules, +membership `in` outside a for header, open-ended ranges and omitted slice bounds +are deferred. The grammar draft's `none` is unsupported; use `()`. +Consult the [TODO list](https://github.com/mini-lang/mini-lang/blob/main/TODO.md) +before extending the language. diff --git a/index.md b/index.md index cf0ce66..c2a1ecf 100644 --- a/index.md +++ b/index.md @@ -23,7 +23,9 @@ layout: home This is the official programming language `mini` website, designed in the [*Write a language in a week*](https://www.mini-lang.org/blog) blog series. `mini` is a minimal programming language that is built for the purpose of teaching programming language design. -The language is dynamically typed and expression-based. It is designed to be easy to learn and use. There are support for both functional and object oriented programming paradigms. +The language is dynamically typed and expression-based. It is designed to be easy to learn and use. The educational v1 subset supports functions, collections and structured control flow. +Classes, enums and pattern matching are deferred. See the [verified reference](/documentation) +and [implementation tutorial](/tutorial/v1). It is not intended to be a full language, but rather a language that can be expanded upon and explore new and more advanced concepts in language design. @@ -33,10 +35,12 @@ It is not intended to be a full language, but rather a language that can be expa The latest version of `mini` can be installed using the commands below: ```sh -> git clone https://github.com/mini-lang/mini-lang && cd mini-lang -> py mini.py +git clone https://github.com/mini-lang/mini-lang +cd mini-lang +python mini.py examples/hello_world.m +python mini.py repl ``` -> `mini` requires [Python](https://www.python.org/) version `3.0` or later. +> `mini` requires [Python](https://www.python.org/) version **3.10 or later**, with no third-party Python dependencies.
diff --git a/tutorial-v1.md b/tutorial-v1.md new file mode 100644 index 0000000..f1ccc5d --- /dev/null +++ b/tutorial-v1.md @@ -0,0 +1,143 @@ +--- +layout: page +title: Implementing the v1 interpreter +permalink: /tutorial/v1 +--- + +# From syntax to working control flow + +This companion to *Write a language in a week* follows the educational v1 +interpreter. The earlier chapters introduce language design; this walkthrough +connects those ideas to the code that actually runs. Start with the +[v1 reference](/documentation) and Python 3.10 or later. + +## Follow the active path + +In the [interpreter repository](https://github.com/mini-lang/mini-lang), +`mini.py` calls `execute` in `src/interpreter.py`. Its lexer and parser imports +are compatibility facades for `src/manual/lexer.py` and `src/manual/parser.py`. +The handwritten parser builds nodes from `src/manual/ast.py`; `src/evaluator.py` +walks them using environments from `src/environment.py`. + +The newer `src/grammar/mini.lark` describes proposed syntax. It has no connection +to execution. Adding a production there does not add a working language feature. +Keep [the capability matrix](https://github.com/mini-lang/mini-lang/blob/main/docs/v1.md) +next to you when experimenting. Classes, enums and pattern matching remain exercises +for a future version. + +## Make a loop able to change its condition + +A block creates a child environment. Reading a variable searches outward through +parents. Assignment must update the nearest existing binding, or a counter changed +inside a loop body would leave the outer condition unchanged. A new name still +belongs to the current scope. `Environment.define` deliberately binds locally for +function parameters and for-loop variables, so those names can shadow outer ones. + +```mini +count = 0 +while count < 3 { + count += 1 +} +print(count) +outer = 99 +for outer in 1..3 { print(outer) } +print(outer) +// => 3 +// => 1 +// => 2 +// => 99 +``` + +The evaluator checks a while condition before each iteration and requires a bool. +A for loop evaluates its collection once. Both loops return `()`. Each iteration +gets a fresh scope, so closures capture separate iteration bindings: + +```mini +functions = [] +for n in 1..4 { + list_append(functions, () => n) +} +print(functions[0](), functions[1](), functions[2]()) +// => 1 2 3 +``` + +Ranges here are eager lists with an excluded upper bound: `1..4` visits 1, 2, 3. +No lazy iterator or pattern binding machinery is needed for this subset. + +## Unwind structured control flow + +A normal expression produces a value. A `break`, `continue` or `return` must +instead stop evaluating the rest of its current block. The evaluator uses small +internal exceptions: `LoopControl` and `ReturnControl`. + +A loop catches only loop control from its body. `break` exits that loop; +`continue` starts the next iteration. The nearest nested loop catches its own +signal first. Function calls catch return signals and yield their values; return +therefore travels through intervening blocks and loops. + +```mini +first_odd(values) = { + for value in values { + if value % 2 == 0 { continue } + return value + } + () +} +print(first_odd([2, 4, 7, 9]), first_odd([])) +// => 7 () +``` + +The parser tracks function and loop-body nesting. When parsing a function body it +resets loop depth: a function declared inside a loop cannot break its caller's +loop. This check also rejects invalid control flow in unreachable branches and +uncalled functions. A new loop inside that function may still use loop control. + +There is no return-from-arbitrary-block rule. A plain block yields its last +expression; `return expression` is function-only. Because whitespace is ignored, +v1 requires an explicit value: write `return ()` for unit. + +## Keep errors separate from control signals + +The lexer records each token's start, including source name, line and column. +AST nodes retain the relevant token. The parser raises a syntax `MiniError`; +the evaluator attaches the innermost expression location to runtime failures. +Internal loop/return signals pass through that error wrapper unchanged. + +For example, running a file named `bad.m` containing `print(missing)` reports: + +```text +bad.m:1:7: Runtime error: identifier 'missing' is not defined +``` + +`execute` raises `MiniError` for embedding callers. The CLI catches it, writes to +stderr, and returns status 1. Successful file execution returns 0. The REPL catches +errors per input and preserves its environment for the next expression. + +## Verify the language and its documentation together + +From the interpreter checkout, run: + +```sh +python test.py +``` + +This runs the legacy tests, all example files, scope/control-flow/diagnostic tests, +and every `mini` fence in the README and `docs/`. Expected output is written in +`// =>` comments; each fence is a standalone program. Tests also check failure +status, so a program that merely prints an error cannot pass as a successful run. + +To include this companion checkout, point the test runner at it: + +```sh +MINI_TUTORIAL_DIR=../mini-lang.github.io python test.py +``` + +The companion's CI runs the same fence test against the interpreter. Merge these +tutorial changes after the v1 interpreter changes so the checked examples agree. + +For another treatment of the same ideas, read *Crafting Interpreters* on +[control flow](https://craftinginterpreters.com/control-flow.html), +[functions](https://craftinginterpreters.com/functions.html), and +[resolving and binding](https://craftinginterpreters.com/resolving-and-binding.html). +Mini has its own expression syntax and scope rules; these chapters explain the +techniques rather than define Mini's behavior. From 956ac6517475bd7e1a21395be85884a5f317e793 Mon Sep 17 00:00:00 2001 From: William <41281398+WilliamRagstad@users.noreply.github.com> Date: Sun, 25 Oct 2026 16:55:33 +0100 Subject: [PATCH 2/2] Preserve the intended language specification alongside the v1 reference --- _posts/2022-03-28-0-intro.md | 2 +- _posts/2022-04-20-1-theory.md | 2 +- documentation-v1.md | 171 ++++++ documentation.md | 943 +++++++++++++++++++++++++++++----- index.md | 7 +- tutorial-v1.md | 9 +- 6 files changed, 986 insertions(+), 148 deletions(-) create mode 100644 documentation-v1.md diff --git a/_posts/2022-03-28-0-intro.md b/_posts/2022-03-28-0-intro.md index 20d39b2..fc3c892 100644 --- a/_posts/2022-03-28-0-intro.md +++ b/_posts/2022-03-28-0-intro.md @@ -19,7 +19,7 @@ permalink: /blog/:title # Introduction > Current implementation: see the [educational v1 walkthrough](/tutorial/v1) -> and [verified reference](/documentation). The interpreter uses the handwritten +> and [verified reference](/documentation/v1). The interpreter uses the handwritten > parser; the Lark grammar is a future design draft. In this short series, together we will implement a programming language in just one week. This is a challenge for all developers out there who are interested in developing their skills and gaining a deeper understanding of the general principles on which programming languages are based. diff --git a/_posts/2022-04-20-1-theory.md b/_posts/2022-04-20-1-theory.md index d0f3b2b..acb5f43 100644 --- a/_posts/2022-04-20-1-theory.md +++ b/_posts/2022-04-20-1-theory.md @@ -20,7 +20,7 @@ permalink: /blog/:title # The Theory > Current implementation: see the [educational v1 walkthrough](/tutorial/v1) -> and [verified reference](/documentation). The interpreter uses the handwritten +> and [verified reference](/documentation/v1). The interpreter uses the handwritten > parser; the Lark grammar is a future design draft. > Warning! This article is still under construction! diff --git a/documentation-v1.md b/documentation-v1.md new file mode 100644 index 0000000..2829a74 --- /dev/null +++ b/documentation-v1.md @@ -0,0 +1,171 @@ +--- +layout: page +title: Educational v1 reference +permalink: /documentation/v1 +--- + +# Educational v1 reference + +Mini is a small, dynamically typed, expression-based interpreter for learning +language implementation. This page describes the **handwritten interpreter**, +which implements a subset of the [intended language design](/documentation). + +Use Python **3.10 or later**. No third-party Python packages are required. + +```sh +git clone https://github.com/mini-lang/mini-lang +cd mini-lang +python mini.py examples/hello_world.m +python mini.py repl +python test.py +``` + +The CLI loads `src/parser.py` and `src/lexer.py`, which re-export `src/manual/`. +`src/evaluator.py` executes the resulting AST. `src/grammar/mini.lark` is an +experimental grammar draft, **not the active parser**. In particular, its colon +bodies, newline rules and pattern syntax do not describe v1. + +See the [capability matrix](https://github.com/mini-lang/mini-lang/blob/main/docs/v1.md) +and [implementation tutorial](/tutorial/v1). Every `mini` code fence below runs in +a fresh environment in the interpreter's documentation tests; `// =>` gives the +exact printed output. + +## Values and expressions + +Values include numbers, booleans, strings, lists, tuples, maps and unit `()`. +Operators include `+ - * / % ^`, comparisons, and short-circuit `and` / `or` +(also `&&` / `||`). Negation is `not` or `!`; single `&` and `|` are unsupported. +`(x)` groups an expression; `(x, y)` is a tuple. A one-element tuple literal is +not supported. Unicode letters are allowed in identifiers; emoji belong in strings. + +Whitespace separates expressions. Newlines are not statement terminators; +semicolons are unsupported. Keep calls and indexes adjacent to their receiver: +`f(x)`, `items[0]`. Comments use `//` or `/* ... */`. + +```mini +π = 3.14 +print(π * 2, not false, "hello 🌍") +print(false and missing) +// => 6.28 true hello 🌍 +// => false +``` + +## Variables, blocks and functions + +`=` creates a local name if no enclosing binding exists; otherwise it updates +the nearest existing binding. Identifier `+=` is supported for numbers and +strings. Other compound assignments are deferred. Underscore-prefixed names +are ordinary variables, not discards. + +Blocks return their last expression, or `()` if empty. New block-local names +do not leak. Functions capture their defining environment, and parameters always +bind locally, shadowing outer names. Functions may use an expression or block body. + +```mini +x = 10 +{ x += 2 local = 99 } +add(x, y) = x + y +square = (x) => x * x +print(x, add(2, 3), square(4)) +// => 12 5 16 +``` + +## Collections and built-ins + +Lists concatenate with `+`; tuples add element-wise; maps merge with `+`. +Indexing supports lists, tuples and maps, including negative list/tuple indexes. +Lists and maps support assignment through computed indexes. Map identifiers are +literal string keys. Integer and boolean literal keys use their string forms. +Slices require explicit start and end; the end is excluded and the step cannot be zero. + +Use free functions such as `list_append`, `list_size`, `list_map`, `list_filter`, +`list_reduce`, `map_size` and `map_contains`. Collection methods such as +`items.append(value)` are not implemented. `print`, `input`, `typeof` and +`assert(condition, message)` are also available. + +```mini +items = [1, 2, 3] +for i in 0..3 { items[i] = items[i] * 2 } +list_append(items, 8) +print(items, items[1:3]) +m = #{name: "Mini"} +key = "name" +m[key] = "Ada" +print(m.name, map_size(m)) +// => [2, 4, 6, 8] [4, 6] +// => Ada 1 +``` + +The conversion functions are `str`, `number`, `int` and `bool`, not `string`. +`number` and `int` return `()` when string conversion fails. The existing `bool` +conversion recognizes the text `"true"` case-insensitively; it is not general +truthiness. Conditions themselves must be booleans. + +```mini +print(str(42), number("1.5"), int("12"), bool("true"), bool(1)) +print(number("invalid")) +// => 42 1.5 12 true false +// => () +``` + +The larger host-oriented standard library is experimental and outside the v1 +example guarantee. + +## Control flow structures + +`if condition { ... } else if condition { ... } else { ... }` returns the chosen +body's value. Without a matching branch or `else`, it returns `()`. +Use braces for clear body boundaries; v1 also retains single-expression bodies +without colons. + +`while condition { ... }` checks a boolean before every iteration. +`for name in values { ... }` evaluates the iterable once and visits list/tuple +elements, string characters or map keys in insertion order. Unit `()` iterates +as an empty tuple. Each iteration has a fresh scope; the for name shadows an +outer name and does not leak. Loops return `()`. + +```mini +n = 0 +while n < 4 { + n += 1 + if n == 2 { continue } + print(n) +} +for key in #{a: 1, b: 2} { print(key) } +// => 1 +// => 3 +// => 4 +// => a +// => b +``` + +`break` exits the nearest loop; `continue` skips to its next iteration. +Neither may cross a function boundary. `return expression` exits the current +function through nested blocks and loops. Use `return ()` for unit. Return from +a top-level block, and loop control outside a loop body, are errors even when +that code would never run. + +## Ranges + +`start..end` produces an **eager, ascending, end-exclusive integer list**. +Both bounds are required. This is not the older proposed inclusive lazy range. + +```mini +print(1..4) +print(4..1) +// => [1, 2, 3] +// => [] +``` + +## Errors and deferred features + +Lexical, syntax and runtime errors include the filename and one-based line and +column. File execution writes errors to stderr and exits nonzero. The REPL +reports an error and accepts the next input; EOF exits it. `--debug` includes +Python tracebacks for interpreter development. + +Classes, enums, pattern matching (`match`, `is`, destructuring), sets, modules, +membership `in` outside a for header, open-ended ranges and omitted slice bounds +are deferred. The grammar draft's `none` is unsupported; use `()`. +Consult the [TODO list](https://github.com/mini-lang/mini-lang/blob/main/TODO.md) +before extending the language. diff --git a/documentation.md b/documentation.md index 64f7013..700b051 100644 --- a/documentation.md +++ b/documentation.md @@ -4,168 +4,833 @@ title: Documentation permalink: /documentation --- -# Educational v1 reference +# Documentation for the `mini` language + +> **Intended language design** +> This specification describes what Mini is intended to become, including features +> beyond the current interpreter. The design and its examples remain part of the project. +> For the executable subset, use the [educational v1 reference](/documentation/v1), +> [implementation tutorial](/tutorial/v1), and +> [capability matrix](https://github.com/mini-lang/mini-lang/blob/main/docs/v1.md). +> Only examples explicitly marked `mini` in the v1 reference and tutorial are +> tested as runnable v1 programs; the design examples below describe intended behavior. + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + +Welcome to the documentation for the `mini` language! This is the place to go if you want to learn more about the language and how to use it. The documentation mainly focus on the language [specification](#specification). + +`mini` is a minimal programming language that is built for the purpose of teaching programming language design. +The language is dynamically typed and expression-based. It is designed to be easy to learn and use. The intended design supports both functional and object oriented programming paradigms. + + + +## Table of contents + +- [Table of contents](#table-of-contents) +- [Features](#features) +- [Specification](#specification) + * [Primitive data types](#primitive-data-types) + * [Variable declaration](#variable-declaration) + * [Comments](#comments) + * [Lambda expressions](#lambda-expressions) + * [Functions](#functions) + * [Collection data structures](#collection-data-structures) + + [More on collection data structures](#more-on-collection-data-structures) + * [Enums](#enums) + * [Classes](#classes) + * [Built-in functions](#built-in-functions) + * [Control flow structures](#control-flow-structures) + * [Other keywords and operators](#other-keywords-and-operators) +- [Contribute! 🎉](#contribute-) + 1. [Perfecting the language 👑](#1-perfecting-the-language-) + 2. [Improve the documentation 📝](#2-improve-the-documentation-) + 3. [Expand the ecosystem 🏕️](#3-expand-the-ecosystem-%EF%B8%8F) + 4. [Get involved in the community 🫂](#4-get-involved-in-the-community-) + 5. [Improve the article series 📚](#5-improve-the-article-series-) +- [License](#license) + +Table of contents generated with markdown-toc + + +
+ +## Features + +The intended language design includes: + +* Basic arithmetic, logic, and comparison operators +* Primitive data types such as `number`, `boolean`, and `string` +* Collection data structures like `list`, `tuple`, `map`, and `set` +* Lambda expressions and functions +* Classes and inheritance +* Enum types +* Variables +* Code blocks +* C-stlye Comments +* A set of built-in functions +* Basic control flow structures +* Other keywords and operators + +## Specification + +The following describes the intended language. See the [v1 reference](/documentation/v1) +for current implementation support and differences. + +The syntax of `mini` is similar to the [ECMAScript specification](https://tc39.es/ecma262/), but not identical. + +### Primitive data types + +There are three primitive data types in `mini`: +* `number` - Represents both floating point and integer numbers + * Arithmetic operators: `+`, `-`, `*`, `/`, `%` (modulo) + * Comparison operators: `==`, `!=`, `<`, `<=`, `>`, `>=` + * Examples: `21`, `4.2`, `-3`, `-3.5` +* `boolean` - Represents logical `true` or `false` + * Logical operators: `&` (and), `|` (or), `!` (not) + * Comparison operators: `==` + * Examples: `true`, `false` +* `string` - Represents a sequence of characters, or a single character + * Arithmetic operators: `+` (concatenation) + * Comparison operators: `==`, `!=` + * Examples: `"hello"`, `'world'`, `"a"`, `'b'` + + +### Variable declaration +A variable declaration is the same as an assignment, but if the variable is not already declared, it is automatically declared. + +```js +x = 10 +``` -Mini is a small, dynamically typed, expression-based interpreter for learning -language implementation. This page describes the **handwritten interpreter**, -not every feature proposed during the project's development. +Multiple variables can be declared with the same value in a single statement. + +```js +x = y = z = 10 +``` -Use Python **3.10 or later**. No third-party Python packages are required. +Or multiple variables can be declared with different values on a single line, must be separated by whitespace. -```sh -git clone https://github.com/mini-lang/mini-lang -cd mini-lang -python mini.py examples/hello_world.m -python mini.py repl -python test.py +```js +x = 10 y = 20 z = 30 ``` -The CLI loads `src/parser.py` and `src/lexer.py`, which re-export `src/manual/`. -`src/evaluator.py` executes the resulting AST. `src/grammar/mini.lark` is an -experimental grammar draft, **not the active parser**. In particular, its colon -bodies, newline rules and pattern syntax do not describe v1. +All variable identifiers **starting** with an underscore `_` are treated as "don't care" variables. Meaning its value is "thrown away" when bound in a scope. -See the [capability matrix](https://github.com/mini-lang/mini-lang/blob/main/docs/v1.md) -and [implementation tutorial](/tutorial/v1). Every `mini` code fence below runs in -a fresh environment in the interpreter's documentation tests; `// =>` gives the -exact printed output. +```ts +_ignored = 10 +print(_ignored) // Error, _ignored is not defined +``` -## Values and expressions +This is useful for catching all values in a pattern match. -Values include numbers, booleans, strings, lists, tuples, maps and unit `()`. -Operators include `+ - * / % ^`, comparisons, and short-circuit `and` / `or` -(also `&&` / `||`). Negation is `not` or `!`; single `&` and `|` are unsupported. -`(x)` groups an expression; `(x, y)` is a tuple. A one-element tuple literal is -not supported. Unicode letters are allowed in identifiers; emoji belong in strings. +```fsharp +match x + | 10 -> "ten" + | _ -> "other" // Catch all values not already matched +``` -Whitespace separates expressions. Newlines are not statement terminators; -semicolons are unsupported. Keep calls and indexes adjacent to their receiver: -`f(x)`, `items[0]`. Comments use `//` or `/* ... */`. +Variables can also be set to the return value of a code block. -```mini -π = 3.14 -print(π * 2, not false, "hello 🌍") -print(false and missing) -// => 6.28 true hello 🌍 -// => false +```js +x = { + y = 10 + y * 2 +} +x // 20 ``` -## Variables, blocks and functions +### Comments -`=` creates a local name if no enclosing binding exists; otherwise it updates -the nearest existing binding. Identifier `+=` is supported for numbers and -strings. Other compound assignments are deferred. Underscore-prefixed names -are ordinary variables, not discards. +```ts +// Single-line comment +/* Multi-line + comment */ +``` -Blocks return their last expression, or `()` if empty. New block-local names -do not leak. Functions capture their defining environment, and parameters always -bind locally, shadowing outer names. Functions may use an expression or block body. +There are also unofficial block comment syntax for documentation. +This is not currently supported in this language, but mainly processed by documentation tools to generate application programming interface (API) documentation. + +```ts +/** + * Documentation comment + * + * @param {number} x + * @param {string} y + * @returns {boolean} + **/ +``` -```mini -x = 10 -{ x += 2 local = 99 } +### Lambda expressions + +Lambdas are declared with the `=>` operator. +The left hand side of the `=>` is the parameter-list tuple, and the right hand side is the function body (A single expression or block). + +```js +(x, y) => x + y +``` + +Empty parameter-list lambdas can be declared with an empty tuple. Used for generator and supplier functions. + +```js +() => 42 +``` + +### Functions + +A function is simply a lambda expression bound to a variable. +Functions can be declared by assigning a lambda expression to a variable as seen below. + +```cs +add = (x, y) => x + y +add(1, 2) +``` + +Alternative syntax sugar for functions is to directly specify the parameter-list and function body separated by an assignment operator. + +```cs add(x, y) = x + y -square = (x) => x * x -print(x, add(2, 3), square(4)) -// => 12 5 16 -``` - -## Collections and built-ins - -Lists concatenate with `+`; tuples add element-wise; maps merge with `+`. -Indexing supports lists, tuples and maps, including negative list/tuple indexes. -Lists and maps support assignment through computed indexes. Map identifiers are -literal string keys. Integer and boolean literal keys use their string forms. -Slices require explicit start and end; the end is excluded and the step cannot be zero. - -Use free functions such as `list_append`, `list_size`, `list_map`, `list_filter`, -`list_reduce`, `map_size` and `map_contains`. Collection methods such as -`items.append(value)` are not implemented. `print`, `input`, `typeof` and -`assert(condition, message)` are also available. - -```mini -items = [1, 2, 3] -for i in 0..3 { items[i] = items[i] * 2 } -list_append(items, 8) -print(items, items[1:3]) -m = #{name: "Mini"} -key = "name" -m[key] = "Ada" -print(m.name, map_size(m)) -// => [2, 4, 6, 8] [4, 6] -// => Ada 1 -``` - -The conversion functions are `str`, `number`, `int` and `bool`, not `string`. -`number` and `int` return `()` when string conversion fails. The existing `bool` -conversion recognizes the text `"true"` case-insensitively; it is not general -truthiness. Conditions themselves must be booleans. - -```mini -print(str(42), number("1.5"), int("12"), bool("true"), bool(1)) -print(number("invalid")) -// => 42 1.5 12 true false -// => () -``` - -The larger host-oriented standard library is experimental and outside the v1 -example guarantee. - -## Control flow structures - -`if condition { ... } else if condition { ... } else { ... }` returns the chosen -body's value. Without a matching branch or `else`, it returns `()`. -Use braces for clear body boundaries; v1 also retains single-expression bodies -without colons. - -`while condition { ... }` checks a boolean before every iteration. -`for name in values { ... }` evaluates the iterable once and visits list/tuple -elements, string characters or map keys in insertion order. Unit `()` iterates -as an empty tuple. Each iteration has a fresh scope; the for name shadows an -outer name and does not leak. Loops return `()`. - -```mini -n = 0 -while n < 4 { - n += 1 - if n == 2 { continue } - print(n) +``` + +These two forms are equivalent as the latter is a shorthand for the former. + +Functions can also have a body block. + +```cs +add(x, y) = { + z = y * 2 + x + z // Return value +} +``` + +`mini` has therefore **higher-order functions**. +As functions and lambdas are the same thing, they can be used interchangeably. The value of a function is the value of the lambda expression. + +```cs +add = (x, y) => x + y // Lambda expression style +add10(y) = add(10, y) // Function style +[1, 2, 3].map(add10) // Function passed as parameter +// [11, 12, 13] +``` + +### Collection data structures + +Primitive data types are useful, but limiting on their own. `mini` has a number of data structures that can be used to store and manipulate collections of data. +* `list` - An ordered collection of values, where each value can be accessed by index. Elements are stored in the order they are declared. + * Examples: + * `[1, 2, 3]` - A list of three integers + * `[true, false, 'hello', 'world']` - Mixed types + * `[]` - An empty list + * `list[0]` - Access the first element + * `list[-1]` - Access the last element + * `list[5] = 10` - Set the sixth element to 10 + * Operators: + * `list1 + list2` - Concatenate two lists + * `list[index]` - Access an element by index (zero-based) + * Methods: + * `list.length` - The number of values in the list + * `list.append(value)` - Adds a value to the end of the list + * `list.prepend(value)` - Adds a value to the beginning of the list + * `list.pop()` - Removes and returns the last value in the list + * `list.shift()` - Removes and returns the first value in the list + * `list.map(fn)` - Returns a new list with the values transformed by the given function + * `list.filter(fn)` - Returns a new list with the values filtered by the given function + * `list.reduce(fn, initialValue)` - Returns a single value by combining the values in the list + * `list.indexOf(value)` - Returns the index of the given value, or `-1` if the value is not in the list + * `list.lastIndexOf(value)` - Returns the index of the last given value, or `-1` if the value is not in the list + * `list.join(separator)` - Returns a string with the values in the list separated by the given separator +* `tuple` - An ordered collection of values, where each value can be accessed by index. + * Examples: + * `(1, 2, 3)` - A tuple of three integers + * `('hello', 'world')` - A tuple of two strings + * `()` - An empty tuple, or `unit`. **This should be treated the same as `null` in other languages.** + * Operators: + * `tuple1 + tuple2` - Add the values of two tuples together (element-wise). + * `tuple[index]` - The value at the given index + * Methods: + * `tuple.length` - The number of values in the tuple +* `map` - A disorderly collection of hashed key-value pairs, where each key can be accessed by index. + * Examples: + * `#{a: 1, b: 2, c: 3}` - A map of three key-value pairs + * `#{0: 1, 1: 2, 2: 3}` - A map with numeric keys + * `#{'hello world': 'hello', 'goodbye world': 'goodbye'}` - A map with string keys + * `#{}` - An empty map + * `map['test'] = 123` - Assigns a value to the given key + * Operators: + * `map1 + map2` - Concatenate two maps together (merge the values of the two maps). + * `map[key]` - The value at the given key + * Methods: + * `map.` - The value associated with the given key, ie. `map.abc` is equivalent to `map['abc']` + * `map.has(key)` - Returns `true` if the map contains the given key + * `map.delete(key)` - Removes the value associated with the given key + * `map.clear()` - Removes all key-value pairs from the map + * `map.keys()` - Returns a new list with the keys in the map + * `map.values()` - Returns a new list with the values in the map + * `map.entries()` - Returns a new list with the key-value pairs in the map + +#### More on collection data structures + +There are many tips and tricks for working with collections. +For example, it is possible to use a list as a stack. + +```ts +stack = [1, 2, 3] +stack.append(4) +print(stack) // [1, 2, 3, 4] +stack.pop() +print(stack.pop()) // 3 +``` + +It is also possible to use a list as a queue. + +```ts +queue = [1, 2, 3] +queue.append(4) +print(queue) // [1, 2, 3, 4] +queue.shift() +print(queue.shift()) // 2 +``` + +Or even a reversed queue. + +```ts +queue = [3, 2, 1] +queue.prepend(4) +print(queue) // [4, 3, 2, 1] +queue.pop() +print(queue.pop()) // 2 +``` + +Lists have support for partial pattern matching, utilizing the `|` operator to separate elements from the rest of the list in a pattern match. +You can extract values from different collections using pattern matching as shown below. + +```fsharp +match [1, 2, 3] + | [f, s | t] => print(f, s, t) // 1 2 [3] + | [h | t] => print(h) // 1 + | [] => print('empty list') // empty list + | _ => print('other') // other type +``` + +```cs +list = [1, 2, 3] +if list is [head | tail] { + print(head) // 1 + print(tail) // [2, 3] +} +``` + +Maps have also support for partial pattern matching. + +```fsharp +match #{a: 1, b: 2, c: 3} + | #{a: f, b: s} => print(f, s) // 1 2 + | #{a: f} => print(f) // 1 + | #{} => print('empty map') // empty map + | _ => print('other') // other type +``` + +> A **Set** is a collection of unique values.\ +> `mini` provides a `set` data structure in its standard library for storing and manipulating unique values in an unordered collection. + +Examples: +* `set(1, 2, 3)` - Initialize a set with three unique values. +* `set()` - The empty set. + +Methods: +* `set.length` - The number of values in the set +* `set.has(value)` - Returns `true` if the set contains the given value +* `set.add(value)` - Adds the given value to the set +* `set.delete(value)` - Removes the given value from the set +* `set.clear()` - Removes all values from the set +* `set.values()` - Returns a new list with the values in the set + + +### Enums + +`mini` supports Rust-like enums. + +```fsharp +enum Letters = A | B | C +``` + +All identifiers (e.g. `A`, `B` and `C`) in an enum must be **unique**. +This means that the names may not occur more than once in the enum declaration. + +> **Example**: *Invalid enum declaration ❌* +> ```fsharp +> enum Optional = A | B | A +> ``` +> The example above would throw an error, since `A` occurs twice in the enum declaration. + +The enum variant arguments/values are assigned in the order they are declared and are *dynamically typed*. This means that the enum variant arguments/values can be of any type. + +```fsharp +enum Optional = Some(value) | None + +x = Some(42) +y = None +z = Some([1, 2, 3]) +``` + +### Classes + +```ts +Animal = class { + Animal(name) = #{ + name: name + } + + speak() { + return "{this.name} makes a noise." + } +} +``` + +In the example above, `Animal` is a class with a constructor and a `speak` method. +The method is defined as a function expression, which has an implicit `this` parameter. + +`mini` also supports basic inheritance. + +```ts +Dog = class : Animal { + Dog(name) { + // Code block with logic other than just a map + return #{ + name: name, + dogSpecificSound: "Woof" + } + } + + speak() { + return "{this.speak()} {this.dogSpecificSound}!" + } +} +``` + +Running this with the code below will produce the following output: + +```ts +dog = Dog("Fido") +dog.speak() // "Fido makes a noise. Woof!" +``` + +Classes are instantiated simliar to function calls, other languages like Python have a similar syntax. + +```ts +animal = Animal("Pig") +animal.speak() // "Pig makes a noise." +animal.name // Ok, returns "Pig" +animal.dogSpecificSound // Throws an error, since dogSpecificSound is not defined on Animal +``` + +Because classes also are expressions, they can be assigned to variables. This is useful for creating objects that are used in multiple places, or for creating objects that are passed to functions. +This is also how static methods can be defined on a class. + +```ts +MyClass = class { + hello() { + return "Hello world!" + } +} +MyClass.hello() // Throw an error, since hello is only defined on MyClass instances +MyClass.bye() // Throws an error, since bye is not defined on MyClass +myInstance = MyClass() +myInstance.hello() // "Hello world!" +myInstance.bye() // Throws an error, since bye is not defined on MyClass instances +// Define a static method on MyClass +MyClass.bye = () => { + return "Bye world!" +} +MyClass.bye() // "Bye world!" +myInstance.bye() // Throws an error, since bye is not defined on MyClass instances byt on MyClass itself +``` +> Note: `mini` does not support static methods inside of class expressions.\ +> Instead, static methods can be defined on the class value itself as shown above for `MyClass.bye`. + + +### Built-in functions + +There are a number of built-in functions that can be used in `mini`: +* `print` - Prints a string to the console + * Examples: + * `print('hello')` + * `print("hello", "world")` + * `print(1, 2, 3)` +* `input` - Gets a string from the console + * Examples: + * `input()` + * `input("Enter your name: ")` + +There are also a number of helper functions to convert values between different types. +* `string` - String representation of a given value + * Examples: + * `string([1, 2, 3])` + * `string(1.23)` + * `string(true)` + * `string(#{'a': 1, 'b': 2})` + * `string("hello")` +* `number` - Integer representation of a given value + * Examples: + * `number('1')` + * `number(1.23)` + * `number(true)` - Returns `1` + * `number(#{'a': 1, 'b': 2})` - Fails with an error + * `number("hello")` - Fails with an error +* `boolean` - Boolean representation of a given value + * Examples: + * `boolean('1')` - Returns `true` + * `boolean(1.23)` - Returns `true` + * `boolean(0)` - Returns `false` + * `boolean(true)` - Returns `true` + * `boolean("")` - Returns `false` + * `boolean(#{'a': 1, 'b': 2})` - Returns `true` + * `boolean("hello")` - Returns `true` + +### Control flow structures + +There are three control flow structures in `mini`: +* `if` - If-then-else statement +* `match` - Pattern matching statement +* `for` - For-loop +* `while` - While-loop +* `break` - Break out of a loop +* `continue` - Continue to the next iteration of a loop +* `return` - Return from a function, last expression in a block is implicitly returned + +Below are examples of each control flow structure. + +> **`if` statement** +> +> The `if` statement is used to execute and return the value of a block of code or a single expression if a condition guard is true. + +The guard is an expression directly following the `if` keyword, surrounding parentheses are optional. After that, a block of code or a single expression is specified. + +```js +if x == 1 "one" +``` + +The example above returns the string `"one"` if the condition `x == 1` is true. + +```js +if x % 2 == 0 { + x *= 2 + return x + 1 +} +``` + +The example above returns the value of `x * 2 + 1` if `x` is even. + +If statements also has the power to pattern match on values using the `is` operator. + +> **`if-else` statement** +> +> The `if-else` statement is used to execute and return the value of different blocks of code or single expressions depending on the value of a condition guard. + +The code below is a short implementation of the absolute value function `|x|`. + +```js +if x > 0 x +else -x +``` + +> **`else if` statement** +> +> The `else if` statements can be used to add additional conditions to an `if-else` statement. + +```fsharp +if x >= 10 "ten or more" +else if x >= 5 "five or more" +else if x >= 1 "one or more" +else "none" +``` + +> **`match` statement** +> +> The `match` statement matches a value against a set of patterns, and returns the value of the first pattern that matches. + +First specify the value to match against, then specify a list of patterns separated by `|` characters. +The first pattern that matches the value is the one that gets evaluated and returned. + +```fsharp +match x + | 1 => "one" + | 2 => "two" + | _ => "many" +``` + +One could think of match as many `if x is p` statements combined into a single statement. + +A match case can also have a guard using the `if` keyword, which ensures that the pattern on the left hand side also satesfies a logical condition on the right hand side, before evaluating the matching case expression. + +```fsharp +match x + | number(n) if n > 0 => "positive" + | number(n) if n < 0 => "negative" + | number(n) if n == 0 => "zero" + | _ => "not a number" +``` + +You can also inline the pattern list, which is useful when the pattern list is short. + +```fsharp +x = Optional.Some(1) +match x | Some(n) => "some" | None => "none" +``` + +> **`for` statement** +> +> The `for` statement is used to iterate over a list of values or an iterator. + +The syntax is similar to the `for` loop in other languages such as Python and Ruby, using the `in` keyword to specify the list or iterable. + +```fsharp +for x in [1, 2, 3] print(x) +``` + +Using a lazy iterator is possible by using the `..` operator. + +```fsharp +for x in 1..10 print(x) +``` + +It is also possible to iterate over a map where the keys are used as the iterator. This is because the map data structure implements the `Iterator` class interface. + +```ts +m = #{a: 1, 'b': 2, "c": 3} +for x in m + print(x) +// prints a, b, c +``` + +Passing an extra pattern argument, `v` for example, to the `for` statement is used to specify a variable to be used as the iterator value. +In this case the variable `k` is the key and `v` is used to store the value of each map entry. + +```ts +m = #{a: 1, 'b': 2, "c": 3} +for (k, v) in m + print(k, v) +// prints a 1, b 2, c 3 +``` + +> **`while` statement** +> +> The `while` statement is used to execute a block of code while a condition is true. + +The guard is an expression directly following the `while` keyword, surrounding parentheses are optional. After that, a block of code or a single expression is specified. + +```ts +while x < 10 x += 1 +``` + +Alternative styles of writing a while loop are also possible. + +```ts +while x < 10 { + x += 1 +} +``` + +```ts +while (x < 10) x += 1 +``` + +Or both. + +```ts +while (x < 10) { + x += 1 +} +``` + +> **`break` statement** +> +> The `break` statement is used to break out of the nearest enclosing loop. + +```ts +while x < 10 { + x += 1 + if x == 5 break +} +``` + + +> **`continue` statement** +> +> The `continue` statement is used to continue to the next iteration of the nearest enclosing loop. + +```ts +while x < 10 { + x += 1 + if x % 2 == 0 continue + print(x) +} +``` +The example above prints all odd numbers from 1 to 10. E.g. 1, 3, 5, 7, 9. + + +> **`return` statement** +> +> The `return` statement is used to return a value from a function or code block. The last expression in a block is implicitly returned. + +```ts +value = { + x = 1 + if condition return x // Early return if condition is true + y = 2 + x + y // Returned implicitly } -for key in #{a: 1, b: 2} { print(key) } -// => 1 -// => 3 -// => 4 -// => a -// => b +// value can either be 1 (early) or 3 (implicit) ``` -`break` exits the nearest loop; `continue` skips to its next iteration. -Neither may cross a function boundary. `return expression` exits the current -function through nested blocks and loops. Use `return ()` for unit. Return from -a top-level block, and loop control outside a loop body, are errors even when -that code would never run. -## Ranges +### Other keywords and operators + +Other than the operators and keywords listed above, there are a number of other keywords and operators that can be used in `mini`. +* `is` - Used to match values against patterns and bind them to variables +* `in` - Used to check if a value is in a set of values +* `..` - Used to specify a range of values + +> **`is` keyword** +> +> The `is` keyword is used to match values against patterns and bind them to variables. + +Use `is` in an if-else statement to pattern match on values and change the control flow of the program depending on the match result. + +```ts +if x is number "number" +else if x is string "string" +else if x is boolean "boolean" +else "unknown" +``` + +Or more specifically, get the type of the value and bind it to a variable. + +```ts +if x is number(n) "number: " + n +else if x is string(s) "string: " + s +else if x is boolean(b) "boolean: " + b +else "unknown: " + x +``` + +Can be used to handle optional values. + +```fsharp +if x is Optional.Some(v) "some: " + v +else "none" +``` + +Or match on data types. + +```ts +if l is [head | tail] "list: " + head + " " + tail +else +``` + +> **`in` keyword** +> +> The `in` keyword is used to check if a value is in a set of values. + +Check if a value is in a list. + +```ts +if x in [1, 2, 3] "in list" +else "not in list" +``` + +Check if a value is in a range. Read more about the `..` operator below. + +```ts +if x in 1..10 "in range" +else "not in range" +``` + +Check if value is a key in a map. If `x` would be `2` for example, `x` would be in the map and the condition would be true. + +```ts +if x in #{1: "one", 2: "two", 3: "three"} "in map" +else "not in map" +``` -`start..end` produces an **eager, ascending, end-exclusive integer list**. -Both bounds are required. This is not the older proposed inclusive lazy range. +Check if a value is in a tuple. -```mini -print(1..4) -print(4..1) -// => [1, 2, 3] -// => [] +```ts +if x in (1, 2, 3) "in tuple" +else "not in tuple" ``` -## Errors and deferred features +One can also use `in` when iterating over a set of values using a `for` loop. + +```ts +for x in [1, 2, 3] print(x) +``` + + +> **`..` Operator** +> +> The `..` operator is used to specify a range of values including the lower and upper bounds. + +```ts +for i in 1..10 { + txt = 'i: ' + i + print(txt) +} +``` + +The example above prints the numbers 1 to 10. + +It is also possible to generate a lazy sequence of values withouth having to store them in memory by excluding the upper bound. + +```ts +for i in 1.. { + if i > 1000 break + txt = 'i: ' + i + print(txt) +} +``` + +## Contribute! 🎉 +Contributions of all kinds are welcome, not only in the form of **code** but also with regards to the official **documentation**, **debugging** help and **tickets/issues** in the bug tracker, support of other users on the **community** forum or the official discord and also **financially**. + +If you think something is bad about the `mini` language or the article series written about it, please **help in any way to make it better** instead of complaining about it. + +For information about how to go about submitting bug reports or pull requests, please see the project's [contribution guidelines](https://github.com/WilliamRagstad/mini-lang/blob/main/CONTRIBUTING.md). + +### 1. Perfecting the language 👑 +There is much more to the language than just the syntax, and there are several ways to improve it. +Optimizing the interpreter and compiler is one of the most important aspects of the tooling for a language. +Do consider contributing to the [mini-lang project](https://github.com/WilliamRagstad/mini-lang) to make your own improvements. + +Do you have your own ideas for improvements? Are they specialized in a certain area or controversial in nature? +Please create your own version of the language by forking it on GitHub and sharing your changes! + +### 2. Improve the documentation 📝 +The documentation is a great source of information for anyone who wants to learn the language. +It is also a great source of information for anyone who wants to contribute to the project. + +If you are a regular user of the language, you can help improve the documentation by creating a pull request to the official [documentation repository](https://github.com/WilliamRagstad/mini-lang). + +### 3. Expand the ecosystem 🏕️ +There are numerous ways of contributing to the `mini` language, one of which is to contribute code to the [official repository](https://github.com/WilliamRagstad/mini-lang), but there are also many other ways to contribute. + +* Consider building projects in `mini` and publish your projects on GitHub. +* Expand on the tools and libraries that are already available for `mini` and publish your own, look at the todo list and the [third-party tools](/TODO.md#Third-party-tools) section. + +### 4. Get involved in the community 🫂 +The best way to show support and help is to get involved in the community! There are many ways to get involved, start by joining the [official discord](https://discord.gg/YzjQZjh) and ask questions, share your ideas and discuss the language there! It is a great place to learn about the language and to get help with any issues you might have, or to even help out with the development of the language. + +### 5. Improve the article series 📚 +If you are a writer and want to contribute to the article series, please see the [contributing to the article series](/CONTRIBUTING#Article-series) section. + +Other ways to contribute to the article series include: +* Commenting on the articles +* Writing articles about the series +* Writing articles about the language and the project -Lexical, syntax and runtime errors include the filename and one-based line and -column. File execution writes errors to stderr and exits nonzero. The REPL -reports an error and accepts the next input; EOF exits it. `--debug` includes -Python tracebacks for interpreter development. +> ### Citation +> Correctly cite and refer to the `mini` programming language and the article series by using the `🗨️ Cite this repository` button in the right-hand sidebar of this repository. -Classes, enums, pattern matching (`match`, `is`, destructuring), sets, modules, -membership `in` outside a for header, open-ended ranges and omitted slice bounds -are deferred. The grammar draft's `none` is unsupported; use `()`. -Consult the [TODO list](https://github.com/mini-lang/mini-lang/blob/main/TODO.md) -before extending the language. +## License +This project is licensed under the [MIT license](https://github.com/WilliamRagstad/mini-lang/blob/main/LICENSE). +The source code is available on [GitHub](https://github.com/WilliamRagstad/mini-lang). +To modify the code, please fork the project and experiment with the language. +Expanding on the language is highly encouraged. If you have any suggestions, please open an issue or pull request. diff --git a/index.md b/index.md index c2a1ecf..dd19117 100644 --- a/index.md +++ b/index.md @@ -23,9 +23,10 @@ layout: home This is the official programming language `mini` website, designed in the [*Write a language in a week*](https://www.mini-lang.org/blog) blog series. `mini` is a minimal programming language that is built for the purpose of teaching programming language design. -The language is dynamically typed and expression-based. It is designed to be easy to learn and use. The educational v1 subset supports functions, collections and structured control flow. -Classes, enums and pattern matching are deferred. See the [verified reference](/documentation) -and [implementation tutorial](/tutorial/v1). +The language is dynamically typed and expression-based. It is designed to be easy to learn and use. The intended design supports both functional and object oriented programming paradigms. +Read the [full language design](/documentation) for that direction. +The current interpreter implements an educational v1 subset: see the +[verified v1 reference](/documentation/v1) and [implementation tutorial](/tutorial/v1). It is not intended to be a full language, but rather a language that can be expanded upon and explore new and more advanced concepts in language design. diff --git a/tutorial-v1.md b/tutorial-v1.md index f1ccc5d..fe03ff0 100644 --- a/tutorial-v1.md +++ b/tutorial-v1.md @@ -9,7 +9,7 @@ permalink: /tutorial/v1 This companion to *Write a language in a week* follows the educational v1 interpreter. The earlier chapters introduce language design; this walkthrough connects those ideas to the code that actually runs. Start with the -[v1 reference](/documentation) and Python 3.10 or later. +[v1 reference](/documentation/v1) and Python 3.10 or later. ## Follow the active path @@ -22,8 +22,8 @@ walks them using environments from `src/environment.py`. The newer `src/grammar/mini.lark` describes proposed syntax. It has no connection to execution. Adding a production there does not add a working language feature. Keep [the capability matrix](https://github.com/mini-lang/mini-lang/blob/main/docs/v1.md) -next to you when experimenting. Classes, enums and pattern matching remain exercises -for a future version. +next to you when experimenting. Classes, enums and pattern matching remain part +of the [intended language design](/documentation), beyond the current v1 subset. ## Make a loop able to change its condition @@ -122,7 +122,8 @@ python test.py ``` This runs the legacy tests, all example files, scope/control-flow/diagnostic tests, -and every `mini` fence in the README and `docs/`. Expected output is written in +and every `mini` fence in the v1 guide and `docs/`. The full language design is +maintained separately in the README and the website specification. Expected output is written in `// =>` comments; each fence is a standalone program. Tests also check failure status, so a program that merely prints an error cannot pass as a successful run.