diff --git a/README.md b/README.md index 0855835..4c9b6cd 100644 --- a/README.md +++ b/README.md @@ -102,13 +102,14 @@ auto-convert `-` to `_` at least. ``` #### 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: +Function pointer types are defined with the `->` form, which a variable number of types as arguments. +Similar to `def`, you can put a `:` in your argument list to delineate between input parameters and +output types. Unlike `def`, if no `:` is present it is assumed that the function does not return a value, +as inference is not possible. ```fennel -(local callback (ttype (-> [[int] int] [int]))) ; compiles to: local callback = { &int, int } -> { int } +(local callback (ttype (-> [int] int : int))) ; compiles to: local callback = { &int, int } -> { int } +(local callback (ttype (-> float))) ; compiles to: local callback = { float } -> {} ``` #### Tuples @@ -135,17 +136,6 @@ it consistently mean "create a quote" everywhere. Not sure.) ### Terra syntax -#### 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 [ptr [int] : [int]] (return (& [ptr 1]))) ; compiles to: terra (ptr : &int): &int return &ptr[1] end -``` - #### var ```fennel (var name initial-value) ; compiles to: var name = initial-value @@ -162,6 +152,18 @@ syntax for _not_ initalizing the variable on declaration. (tset struct (getfield) value) ; compiles to: struct.[getfield()] = value ``` +#### 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 [nested [[int]]] (return [[nested] 3])) ; compiles to: terra (nested : &&int) return (@nested)[3] end +(def [ptr [int] : [int]] (return (& [ptr 1]))) ; compiles to: terra (ptr : &int): &int return &ptr[1] end +``` + #### field access ```fennel struct.field ; compiles to: struct.field @@ -229,4 +231,13 @@ type. | `(<< x y)` | `x << y` | arithmetic shift x left by y bits | | `(>> x y)` | `x >> y` | arithmetic shift x right by y bits | +#### return +Terra functions can return multiple values, or none. Returns must be explicitly +written with the `return` form. + +```fennel +(def [: int] (return 5)) ; compiles to: terra (): { int } return 5 end +(def [: float float] (return -1.5 1.5)) ; compiles to: terra (): { float float } return -1.5, 1.5 end +``` + #### Fennel escaping diff --git a/go.fnl b/go.fnl index 45693ca..944e053 100644 --- a/go.fnl +++ b/go.fnl @@ -16,3 +16,7 @@ (print result result.real result.imag (get-real result)) (set-real result 10) (print (get-real result))) + +(print (unterra + (def [: int bool] (return 5 true)) +)) diff --git a/terra.fnl b/terra.fnl index 508e7c1..51c123f 100644 --- a/terra.fnl +++ b/terra.fnl @@ -58,9 +58,9 @@ (self:pop) result) :expr (fn [self expr] - (if (and (sym? expr) (not (multi-sym? expr))) (self:env-ref expr) - (= (type expr) :number) (tostring expr) - (= expr nil) :nil + (if (and (sym? expr) (not (multi-sym? expr))) (self:env-ref expr) + (or (= (type expr) :number) (= (type expr) :boolean)) (tostring expr) + (= expr nil) :nil (let [name (safesym :inline-expr) arglist (sequence) expr (extract.quotes expr arglist self.locals) @@ -113,7 +113,7 @@ (where symbol (sym? symbol)) (scope:reference symbol) - (where lit (or (= (type lit) :string) (= (type lit) :number) (= (type lit) :nil))) + (where lit (or (= (type lit) :string) (= (type lit) :number) (= (type lit) :nil) (= (type lit) :boolean))) (scope:expr lit) _ (error (.. "Failed to parse expression: " (view form))))) @@ -152,13 +152,29 @@ _ (error (.. "Invalid field name: " (view name))))) +(fn find-ival [tbl pred] + (var ival nil) + (each [i val (ipairs tbl)] + (when (pred val) (set ival i))) + ival) + +(fn split-arglist [arglist] + (let [iarg-return-sep (find-ival arglist #(and (sym? $1) (= (tostring $1) ":"))) + in (icollect [i arg (ipairs arglist) &until (= i iarg-return-sep)] arg) + out (when (not= iarg-return-sep nil) (icollect [i arg (ipairs arglist)] (when (> i iarg-return-sep) arg)))] + (values in out))) + (fn arglist-type [arglist scope] (.. "{ " (commasep (or arglist []) #(comp.type $1 scope)) " }")) -(tset type-constructors :-> (fn [[in out] scope] (.. (arglist-type in scope) " -> " (arglist-type out scope)))) +(tset type-constructors :-> (fn [argtyps scope] + (let [(in out) (split-arglist argtyps)] + (.. (arglist-type in scope) " -> " (arglist-type (or out []) scope))))) (tset type-constructors :& (fn [[typ] scope] (.. :& (comp.type typ scope)))) (tset type-constructors :$ (fn [typs scope] (.. "tuple(" (commasep typs #(comp.type $1 scope)) ")"))) (fn type-constructors.unquote [[expr] scope] (scope:expr expr)) -(fn forms.rawterra [[text] scope] text) +(fn forms.raw-escape [[text] scope] text) +(fn type-constructors.raw-escape [[text] scope] text) + (fn forms.var [defn scope] (case defn [name typ initval] (.. "var " (scope:addlocal name) " : " (comp.type typ scope) " = " (comp.expr initval scope)) @@ -167,22 +183,15 @@ (fn forms.do [stmts scope] (scope:with #(block :do stmts :end scope #(comp.expr $1 scope)))) -(fn find-ival [tbl pred] - (var ival nil) - (each [i val (ipairs tbl)] - (when (pred val) (set ival i))) - ival) - (fn forms.def [[arglist & stmts] scope] - (scope:with #(let [iarg-return-sep (find-ival arglist #(and (sym? $1) (= (tostring $1) ":"))) - argpairs (fcollect [i 1 (length arglist) 2 &until (= i iarg-return-sep)] - {:name (. arglist i) :type (. arglist (+ i 1))}) - rettyps (when iarg-return-sep (icollect [i typ (ipairs arglist)] (when (> i iarg-return-sep) typ))) - rettyp (if rettyps (.. ": { " (commasep rettyps #(comp.type $1 scope)) " }") "") + (scope:with #(let [(in out) (split-arglist arglist) + argpairs (fcollect [i 1 (length in) 2] {:name (. in i) :type (. in (+ i 1))}) + rettyp (if out (.. ": { " (commasep out #(comp.type $1 scope)) " }") "") argdefs (commasep argpairs #(.. (scope:addlocal $1.name) " : " (comp.type $1.type scope)))] (block (.. "terra (" argdefs ")" rettyp) stmts :end scope #(comp.expr $1 scope))))) -(fn forms.return [[expr] scope] (.. "return " (comp.expr expr scope))) +(fn forms.return [vals scope] (.. "return " (commasep vals #(comp.expr $1 scope)))) + (fn forms.cast [[typ expr] scope] (.. "([" (comp.type typ scope)"](" (comp.expr expr scope) "))")) (fn forms.unquote [[expr] scope] (.. "([" (scope:expr expr) "])"))