157 lines
6 KiB
Markdown
157 lines
6 KiB
Markdown
# Garden
|
|
An experiment in combining [Fennel](https://fennel-lang.org) and [Terra](https://terralang.org).
|
|
|
|
## Rationale
|
|
Idk, seemed cool
|
|
|
|
## Usage
|
|
At the top of your file, include the following:
|
|
```fennel
|
|
(import-macros {: def : q : ttype : static} :terra)
|
|
```
|
|
The `def` macro defines a new terra function. The `q` macro defines a quoted terra expression.
|
|
The `ttype` macro allows you to specify terra type definitions that can't be expressed with
|
|
regular lua syntax. The `static` macro allows you to define "global" terra variables.
|
|
|
|
Notably, all of these macros return values, and none of them define new variables, local or global.
|
|
I could maybe be persuaded to make `def` work like `fn` and optionally define a local, but for now,
|
|
eh, whatever.
|
|
|
|
### def
|
|
Defines a function, compiling down to the `terra` keyword.
|
|
|
|
Syntax:
|
|
```fennel
|
|
(def [argname1 argtype1 argname2 argtype2... : rettype1 rettype2...] statement...)
|
|
```
|
|
|
|
Simple example:
|
|
```fennel
|
|
(local add (def [x int y int : int]
|
|
(return (+ x y))))
|
|
|
|
; compiles to:
|
|
; local add = terra(x : int, y : int) : {int}
|
|
; return (x + y)
|
|
; end
|
|
|
|
(add 1 2) ; returns 3
|
|
```
|
|
|
|
To define a function as returning "void", simply end the argument list with a `:`.
|
|
To make terra infer the return type, do not include a `:` in the argument list at all.
|
|
Unlike Fennel, we do not implement implicit return semantics, and early returns are A-OK.
|
|
Sorry Phil.
|
|
|
|
### q
|
|
Defines a terra quotation, compiling down to the ` `` ` operator if given one argument, and
|
|
`quote` / `end` if given more than one.
|
|
|
|
```fennel
|
|
(fn inc [x] (q (+ x 1))) ; compiles to: function(x) `(x + 1) end
|
|
|
|
```
|
|
|
|
### Type syntax
|
|
In Terra, types are Lua values and can be constructed from regular Lua code, outside of
|
|
`terra` or `quote` blocks. (There are certain places inside these blocks where types can
|
|
be constructed as well.) However, because Terra significantly extends the syntax of Lua to
|
|
allow for convenient type construction, Garden also must provide a mini-language to support
|
|
it.
|
|
|
|
Inside `terra` or `quote` blocks, whenever you have a form that requires a type to be passed
|
|
to it, the compiler will automatically enter a type-compiling context. Outside of these blocks,
|
|
the `ttype` macro can be used.
|
|
|
|
#### Pointers and arrays
|
|
Pointers to types and arrays of types use the Fennel "sequence table" syntax. Alternatively,
|
|
the `&` operator can be used.
|
|
```fennel
|
|
(local intptr (ttype [int])) ; compiles to: &int
|
|
(local intptr2 (ttype (& int))) ; also compiles to: &int
|
|
(local intarray (ttype [int 16])) ; compiles to: int[16]
|
|
```
|
|
|
|
#### Structs
|
|
Structs are defined using the Fennel "key-value table" syntax. For each row in the table,
|
|
the keys are Fennel symbols representing the name of the field, and the values are type
|
|
expressions. If, instead of a symbol, the compiler finds the string `:union`, the value
|
|
is expected to be another "key-value table" containing the same structure. Struct definitons
|
|
can be nested.
|
|
|
|
Note that the field names must be valid Lua symbols; no name-mangling is done. Might be
|
|
worth doing that at some point; it seems likely that I will be annoyed if we don't
|
|
auto-convert `-` to `_` at least.
|
|
|
|
```fennel
|
|
(local Variant (ttype {tag int
|
|
:union {number float
|
|
string [int8]
|
|
complex {real float imag float}}}))
|
|
; compiles to:
|
|
; local Variant = struct {
|
|
; tag : int,
|
|
; union {
|
|
; number : float,
|
|
; string : &int8,
|
|
; complex : struct {
|
|
; real : float,
|
|
; imag : float
|
|
; }
|
|
; }
|
|
; }
|
|
```
|
|
|
|
#### Function pointers
|
|
Function pointer types are defined with the `->` form, which accepts two arguments - a sequence
|
|
of types representing the input parameter types, and a sequence of types representing the return
|
|
value. Actually this kind of sucks, I think we should use the same syntax as `def`. I'll probably
|
|
change this. But this is how it works right now:
|
|
|
|
```fennel
|
|
(local callback (ttype (-> [[int] int] [int]))) ; compiles to: local callback = { &int, int } -> { int }
|
|
```
|
|
|
|
#### Tuples
|
|
Tuple types are defined in Terra with a generic Lua function call to `tuple` that takes a variable
|
|
number of types as parameters. This is supported directly, like any type declaration consisting of
|
|
a function call, but there is a shortened form using `$` to match the tuple instantiation syntax.
|
|
|
|
```fennel
|
|
(ttype (tuple int [int])) ; compiles to: tuple(int, &int)
|
|
(ttype ($ int [int])) ; compiles to: tuple(int, &int)
|
|
```
|
|
|
|
#### Escaping
|
|
Arbitrary Fennel expressions can be evaluated in a type-compilation context using Fennel's `,` prefix,
|
|
which is normally used by macros. If you need to re-enter a type-compilation context after escaping,
|
|
you'll need to nest a call to `ttype`. (I'm considering using ` `` ` for this purpose, but I might have
|
|
it consistently mean "create a quote" everywhere. Not sure.)
|
|
|
|
```fennel
|
|
(ttype ($ [int] (fn-accepting-type [int]) ,(fn-accepting-seq [5]) (fn-accepting-seq-of-types ,[(ttype [int])])))
|
|
; compiles to:
|
|
; tuple(&int, fn_accepting_type(&int), fn_accepting_seq({ 5 }), fn_accepting_seq_of_types({ &int }))
|
|
```
|
|
|
|
### Terra syntax
|
|
|
|
#### var
|
|
```fennel
|
|
(var name initial-value) ; compiles to: var name = initial-value
|
|
(var name type initial-value) ; compiles to: var name : type = initial-value
|
|
```
|
|
Define a local variable named `var`, and set its initial value to `initial-value`. You can
|
|
manually specify a `type`, or you can let terra infer it from `initial-value`. There is no
|
|
syntax for _not_ initalizing the variable on declaration.
|
|
|
|
#### Pointers and arrays
|
|
Dereferencing a pointer or accessing an element in an array uses the same syntax as defining a pointer
|
|
or array type - the Fennel sequence literal. To take a reference to a value, you can use the `&` form.
|
|
|
|
```fennel
|
|
(def [ptr [int]] (return [ptr])) ; compiles to: terra (ptr : &int) return @ptr end
|
|
(def [arr [int 8]] (return [arr 5])) ; compiles to: terra (arr : int[8]) return arr[5] end
|
|
(def [nested [[int]]] (return [nested 0 3])) ; compiles to: terra (nested : &&int) return nested[0][3] end
|
|
(def [])
|
|
``` |