Implement quoting of terra symbols inside lua escapes
This commit is contained in:
parent
28f0289a6b
commit
95d279e45e
56
README.md
Normal file
56
README.md
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# 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} :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.
|
||||||
|
|
||||||
|
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))))
|
||||||
|
(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.
|
||||||
|
|
||||||
|
### syntax within `def` and `q` forms
|
||||||
|
|
||||||
|
```fennel
|
||||||
|
(var name initial-value)
|
||||||
|
(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.
|
||||||
|
|
||||||
|
```fennel
|
||||||
|
|
||||||
|
```
|
29
go.fnl
29
go.fnl
|
@ -1,28 +1,15 @@
|
||||||
(local fennel (require :fennel))
|
(local fennel (require :fennel))
|
||||||
(import-macros {: terra : unterra : untype : def : q : ttype} :terra)
|
(import-macros {: terra : unterra : untype : def : q : ttype} :terra)
|
||||||
|
|
||||||
; (local N 5)
|
(local N 5)
|
||||||
; (print (unterra
|
(fn inc [x] (q (+ ,x 1)))
|
||||||
; (def [x [int] : int64]
|
(print (unterra
|
||||||
; (var y (+ x N))
|
(def [x int]
|
||||||
; (return (cast int64 (& [y]))))))
|
(return ,(inc `x)))))
|
||||||
(print (untype
|
|
||||||
{key int
|
|
||||||
:union {
|
|
||||||
string [int8]
|
|
||||||
number float
|
|
||||||
complex {real float imag float}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
(print (fennel.view (ttype
|
(print
|
||||||
{key int
|
(def [x int]
|
||||||
:union {
|
(return ,(inc `x))))
|
||||||
string [int8]
|
|
||||||
number float
|
|
||||||
complex {real float imag float}
|
|
||||||
}
|
|
||||||
})))
|
|
||||||
|
|
||||||
; (local inc (def [x [int]] (return [x N])))
|
; (local inc (def [x [int]] (return [x N])))
|
||||||
; (print (inc 5))
|
; (print (inc 5))
|
||||||
|
|
53
terra.fnl
53
terra.fnl
|
@ -7,6 +7,35 @@
|
||||||
(fn kv-table? [tbl] ; this should be exported to macros but is not ;_;
|
(fn kv-table? [tbl] ; this should be exported to macros but is not ;_;
|
||||||
(and (table? tbl) (not (sequence? tbl))))
|
(and (table? tbl) (not (sequence? tbl))))
|
||||||
|
|
||||||
|
(fn commasep [list f ?sep]
|
||||||
|
(table.concat (icollect [_ v (ipairs list)] (f v)) (or ?sep ", ")))
|
||||||
|
(fn kvcommasep [tbl f ?sep]
|
||||||
|
(commasep (. (getmetatable tbl) :keys) #(f $1 (. tbl $1)) ?sep))
|
||||||
|
|
||||||
|
(local extract {})
|
||||||
|
(fn extract.quotes-in-table [into tbl inputs locals]
|
||||||
|
(collect [k v (pairs tbl) &into into] k (extract.quotes v inputs locals)))
|
||||||
|
|
||||||
|
(fn extract.ensure-input [input inputs]
|
||||||
|
(var found false)
|
||||||
|
(each [_ sym (ipairs inputs)]
|
||||||
|
(when (= sym input) (set found true)))
|
||||||
|
(when (not found) (table.insert inputs input))
|
||||||
|
input)
|
||||||
|
|
||||||
|
(fn extract.quotes [form inputs locals]
|
||||||
|
(case form
|
||||||
|
(where [q ref] (list? form) (sym? q) (sym? ref) (= (tostring q) :quote))
|
||||||
|
(let [localref (. locals (tostring ref))]
|
||||||
|
(if localref (extract.ensure-input (sym localref) inputs)
|
||||||
|
(error (.. "Unknown local: " (tostring ref)))))
|
||||||
|
|
||||||
|
(where l (list? l)) (extract.quotes-in-table (list) l inputs locals)
|
||||||
|
(where s (sequence? s)) (extract.quotes-in-table (sequence) s inputs locals)
|
||||||
|
(where t (kv-table? t)) (extract.quotes-in-table {} t inputs locals)
|
||||||
|
|
||||||
|
_ form))
|
||||||
|
|
||||||
(fn new-scope []
|
(fn new-scope []
|
||||||
{:locals {}
|
{:locals {}
|
||||||
:env {}
|
:env {}
|
||||||
|
@ -17,9 +46,14 @@
|
||||||
(if (and (sym? expr) (not (multi-sym? expr))) (self:env-ref expr)
|
(if (and (sym? expr) (not (multi-sym? expr))) (self:env-ref expr)
|
||||||
(= (type expr) :number) (tostring expr)
|
(= (type expr) :number) (tostring expr)
|
||||||
(= expr nil) :nil
|
(= expr nil) :nil
|
||||||
(let [name (safesym :inline-expr)]
|
(let [name (safesym :inline-expr)
|
||||||
|
arglist (sequence)
|
||||||
|
expr (extract.quotes expr arglist self.locals)
|
||||||
|
bare-ref? (= (length arglist) 0)
|
||||||
|
expr (if bare-ref? expr `(fn ,arglist ,expr))
|
||||||
|
ref (if bare-ref? name (.. name "(" (commasep arglist #(tostring $1)) ")"))]
|
||||||
(table.insert self.input {: name : expr})
|
(table.insert self.input {: name : expr})
|
||||||
name)))
|
ref)))
|
||||||
:env-ref (fn [self symbol]
|
:env-ref (fn [self symbol]
|
||||||
(let [name (tostring symbol)
|
(let [name (tostring symbol)
|
||||||
loc (. self.env name)]
|
loc (. self.env name)]
|
||||||
|
@ -41,11 +75,6 @@
|
||||||
(local comp {})
|
(local comp {})
|
||||||
(local forms {})
|
(local forms {})
|
||||||
|
|
||||||
(fn commasep [list f]
|
|
||||||
(table.concat (icollect [_ v (ipairs list)] (f v)) ", "))
|
|
||||||
(fn kvcommasep [tbl f]
|
|
||||||
(commasep (. (getmetatable tbl) :keys) #(f $1 (. tbl $1))))
|
|
||||||
|
|
||||||
(fn comp.expr [form scope]
|
(fn comp.expr [form scope]
|
||||||
(case form
|
(case form
|
||||||
(where [head & rest] (sym? head) (list? form) (. forms (tostring head)))
|
(where [head & rest] (sym? head) (list? form) (. forms (tostring head)))
|
||||||
|
@ -112,7 +141,7 @@
|
||||||
(tset type-constructors :-> (fn [[in out] scope] (.. (arglist-type in scope) " -> " (arglist-type out scope))))
|
(tset type-constructors :-> (fn [[in out] scope] (.. (arglist-type in scope) " -> " (arglist-type out scope))))
|
||||||
(tset type-constructors :& (fn [[typ] scope] (.. :& (comp.type typ scope))))
|
(tset type-constructors :& (fn [[typ] scope] (.. :& (comp.type typ scope))))
|
||||||
(tset type-constructors :$ (fn [typs scope] (.. "tuple(" (commasep typs #(comp.type $1 scope)) ")")))
|
(tset type-constructors :$ (fn [typs scope] (.. "tuple(" (commasep typs #(comp.type $1 scope)) ")")))
|
||||||
(fn type-constructors.hashfn [[expr] scope] (scope:expr expr))
|
(fn type-constructors.unquote [[expr] scope] (scope:expr expr))
|
||||||
|
|
||||||
(fn forms.rawterra [[text] scope] text)
|
(fn forms.rawterra [[text] scope] text)
|
||||||
(fn forms.var [defn scope]
|
(fn forms.var [defn scope]
|
||||||
|
@ -147,7 +176,7 @@
|
||||||
(fn forms.return [[expr] scope] (.. "return " (comp.expr expr scope)))
|
(fn forms.return [[expr] scope] (.. "return " (comp.expr expr scope)))
|
||||||
(fn forms.cast [[typ expr] scope]
|
(fn forms.cast [[typ expr] scope]
|
||||||
(.. "([" (comp.type typ scope)"](" (comp.expr expr scope) "))"))
|
(.. "([" (comp.type typ scope)"](" (comp.expr expr scope) "))"))
|
||||||
(fn forms.hashfn [[expr] scope] (.. "([" (scope:expr expr) "])"))
|
(fn forms.unquote [[expr] scope] (.. "([" (scope:expr expr) "])"))
|
||||||
|
|
||||||
(tset forms :& (fn [[expr] scope] (.. :& (comp.expr expr scope))))
|
(tset forms :& (fn [[expr] scope] (.. :& (comp.expr expr scope))))
|
||||||
(tset forms :$ (fn [items scope] (.. "{ " (commasep items #(comp.expr $1 scope)) " }")))
|
(tset forms :$ (fn [items scope] (.. "{ " (commasep items #(comp.expr $1 scope)) " }")))
|
||||||
|
@ -161,6 +190,10 @@
|
||||||
(def-infix :not= "~=")
|
(def-infix :not= "~=")
|
||||||
(def-infix :set :=)
|
(def-infix :set :=)
|
||||||
|
|
||||||
|
(fn comp.quote [stmts scope]
|
||||||
|
(if (= (length stmts) 1) (.. "`(" (comp.expr (. stmts 1) scope) ")")
|
||||||
|
(.. "quote\n" (commasep stmts #(comp.expr $1 scope) "\n") "\nend")))
|
||||||
|
|
||||||
(fn build [expr compiler]
|
(fn build [expr compiler]
|
||||||
(let [scope (new-scope)
|
(let [scope (new-scope)
|
||||||
terra-expr (compiler expr scope)
|
terra-expr (compiler expr scope)
|
||||||
|
@ -175,7 +208,7 @@
|
||||||
(fn ttype [expr] (build expr comp.type))
|
(fn ttype [expr] (build expr comp.type))
|
||||||
|
|
||||||
(fn def [...] (terra `(,(sym :def) ,...)))
|
(fn def [...] (terra `(,(sym :def) ,...)))
|
||||||
(fn q [...] (terra `(,(sym :quote) ,...)))
|
(fn q [...] (build [...] comp.quote))
|
||||||
(fn unterra [...] (view (macroexpand (terra ...))))
|
(fn unterra [...] (view (macroexpand (terra ...))))
|
||||||
(fn untype [...] (view (macroexpand (ttype ...))))
|
(fn untype [...] (view (macroexpand (ttype ...))))
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue