tada/README.md
2022-11-18 17:59:11 +01:00

155 lines
4.5 KiB
Markdown

# Tada
Tada is an interpreted language inspired by Lua.
It started with the idea of making Lua more consistent. For example, why not
return multiple values by returning and then destructuring a table? That would
also allow accessing return values besides the first in expressions. Since we
need destructuring anyways, why not use a table to pass arguments? Positional
and keyword arguments would directly fall out of that approach.
After some discussion, this turned into using tables as answer for pretty much
every design decision. Functions? Tables. Code blocks? Tables, their curly
braces fit pretty well already. Scopes? Tables. Function calls? Tables. Source
code? Tables, with most "normal" syntax being simple syntactic sugar.
## Syntax
At its core, a program consists only of literals. Any further syntax desugars to
literals. A program file contains a single literal, usually a table literal, at
its root.
The types of literal are:
- Boolean: `true`, `false`
- Integer: `1`, `-3`, `0x45`, `0b11001`
- String: `"Hello world\n"`
- Builtin: `'get`, `'scope`
- Table: `'{ 1, 2, 3 }`, `'{ foo: bar }`
A program's source code can be represented entirely with those literals and
nothing else. However, that becomes cumbersome quickly. It doesn't look like an
imperative language either. For that reason there is a lot of syntactic sugar.
## Syntactic sugar
Builtins, function calls and similar operations are magic. On the other hand,
syntactic sugar is the opposite of magic: It is transparent even for those not
familiar with the implementation of the language. When it is applied, the result
is just code again.
One design principle of this language is to limit the scope of magic and rely
mostly on syntactic sugar. This should also make understanding things you don't
know yet easier: It is either syntactic sugar and can be applied, or it is magic
with side effects limited to a local area, making it easy to find and look up.
In the following sections,
- `a`, `b`, ... are arbitrary expressions,
- `foo`, `bar`, ... are identifiers, and
- `{..}` represents an arbitrary list constructor.
### Function calls
| Sugar | Desugared |
|---------|------------------------|
| `a(b)` | `'{ call: a, arg: b }` |
| `a()` | `a(nil)` |
| `a{..}` | `a({..})` |
### Field access
| Sugar | Desugared |
|-------------|-----------------|
| `a[b]` | `'get{a, b}` |
| `a[b] = c` | `'set{a, b, c}` |
| `a.foo` | `a["foo"]` |
| `a.foo = b` | `a["foo"] = b` |
### Variable access
| Sugar | Desugared |
|-----------------|---------------------------|
| `[a]` | `'scope()[a]` |
| `[a] = b` | `'scope()[a] = b` |
| `local [a] = b` | `'setraw('scope(), a, b)` |
| `foo` | `["foo"]` |
| `foo = a` | `["foo"] = a` |
| `local foo = a` | `local ["foo"] = a` |
### Table constructors
| Sugar | Desugared |
|--------------------|-------------------------------|
| `{ a, b, foo: c }` | `'{ raw: '{ a, b, foo: c } }` |
| `{ .., [a] = b }` | `'set({..}, a, b)` |
### Table destructuring
`{ a, b, foo: c } = d` is converted to
```
'{
local expr = 'arg()[0],
local scope = 'arg()[1],
scope.a = expr[0],
scope.b = expr[1],
scope.c = expr.foo,
}{d, 'scope()}
```
which is approximately equal to
```
(function{expr, scope} '{
scope.a = expr[0],
scope.b = expr[1],
scope.c = expr.foo,
}){d, 'scope()}
```
`local { a, b, foo: c } = d` is converted to
```
'{
local expr = 'arg()[0],
local scope = 'arg()[1],
'setraw(scope, "a", expr[0]),
'setraw(scope, "b", expr[1]),
'setraw(scope, "c", expr.foo),
}{d, 'scope()}
```
which is approximately equal to
```
(function{expr, scope} '{
'setraw(scope, "a", expr[0]),
'setraw(scope, "b", expr[1]),
'setraw(scope, "c", expr.foo),
}){d, 'scope()}
```
### Function definitions
`function() a` is converted to
```
{
'{ quote: a },
scope: 'scope(),
}
```
`function(foo) a` is converted to
```
function() '{
local foo = 'arg(),
a
}
```
`function{..} a` is converted to
```
function() '{
local {..} = 'arg(),
a
}
```
| Sugar | Desugared |
|------------------------|---------------------------------|
| `function foo() a` | `foo = function() a` |
| `function foo(a) b` | `foo = function(a) b` |
| `function foo{..} a` | `foo = function{..} a` |