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..fc3c892 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/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. > 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..acb5f43 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/v1). 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-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 8be92c5..700b051 100644 --- a/documentation.md +++ b/documentation.md @@ -6,12 +6,21 @@ permalink: /documentation # 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. 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 intended design supports both functional and object oriented programming paradigms. @@ -47,7 +56,7 @@ The language is dynamically typed and expression-based. It is designed to be eas ## Features -The language has a rich set of features, including: +The intended language design includes: * Basic arithmetic, logic, and comparison operators * Primitive data types such as `number`, `boolean`, and `string` @@ -64,6 +73,9 @@ The language has a rich set of features, including: ## 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 diff --git a/index.md b/index.md index cf0ce66..dd19117 100644 --- a/index.md +++ b/index.md @@ -23,7 +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. 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 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. @@ -33,10 +36,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..fe03ff0 --- /dev/null +++ b/tutorial-v1.md @@ -0,0 +1,144 @@ +--- +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/v1) 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 part +of the [intended language design](/documentation), beyond the current v1 subset. + +## 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 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. + +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.