diff --git a/README.md b/README.md index 4c9b6cd..07d5825 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,15 @@ I could maybe be persuaded to make `def` work like `fn` and optionally define a eh, whatever. ### def -Defines a function, compiling down to the `terra` keyword. +Defines a function, compiling down to the `terra` keyword. Can also be used to create an undefined +function, if called with no statements. -Syntax: ```fennel +; syntax: (def [argname1 argtype1 argname2 argtype2... : rettype1 rettype2...] statement...) -``` +(def [argtype1 argtype2... : rettype1 rettype2...]) -Simple example: -```fennel +; examples: (local add (def [x int y int : int] (return (+ x y)))) @@ -36,10 +36,31 @@ Simple example: ; end (add 1 2) ; returns 3 + +(local iseven (def [uint32 : bool])) +(local isodd (def [n uint32 : bool] + (if (= n 0) + (return true) + (return (iseven (- n 1)))))) +(iseven:adddefinition (def [n uint32 : bool] + (if (= n 0) + (return false) + (return (isodd (- n 1)))))) + +; compiles to: +; local terra iseven :: { uint32 } -> { bool } +; local isodd = terra(n : uint32) : { bool } +; if n == 0 then return true else return iseven(n - 1) end +; end +; iseven:adddefinition(terra(n : uint32) : { bool } +; if n == 0 then return false else return isodd(n - 1) end +; end) ``` 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. +To make terra infer the return type, do not include a `:` in the argument list at all. +Undefined functions can't have their return types inferred. + Unlike Fennel, we do not implement implicit return semantics, and early returns are A-OK. Sorry Phil. diff --git a/go.fnl b/go.fnl index 944e053..43dc9f0 100644 --- a/go.fnl +++ b/go.fnl @@ -1,22 +1,15 @@ (local fennel (require :fennel)) (import-macros {: def : q : ttype : static : unterra : untype} :terra) -(local Complex (ttype {real float imag float})) -(print (unterra - (def [: Complex] (return (Complex { real 5 imag 1 }))) -)) -(print (unterra - (def [c Complex r float] (tset c :real r)) -)) -(local thing (def [: Complex] (return (Complex { real 5 imag 1 })))) -(local get-real (def [val Complex] (return (. val :real)))) -(local set-real (def [c [Complex] r float] (tset c :real r))) - -(let [result (thing)] - (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)) -)) +(local iseven (def [uint32 : bool])) +(local isodd (def [n uint32 : bool] + (if (= n 0) + (return true) + (return (iseven (- n 1)))))) +(iseven:adddefinition (def [n uint32 : bool] + (if (= n 0) + (return false) + (return (isodd (- n 1)))))) +(print iseven) +(print (iseven 5) (isodd 5)) +(print (iseven 6) (isodd 6)) diff --git a/terra.fnl b/terra.fnl index 51c123f..ce20130 100644 --- a/terra.fnl +++ b/terra.fnl @@ -184,11 +184,13 @@ (scope:with #(block :do stmts :end scope #(comp.expr $1 scope)))) (fn forms.def [[arglist & stmts] 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))))) + (if (> (length stmts) 0) + (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)))) + (.. "(function () local terra anonfn :: " ((. type-constructors "->") arglist scope) " return anonfn end)()"))) (fn forms.return [vals scope] (.. "return " (commasep vals #(comp.expr $1 scope)))) @@ -220,6 +222,22 @@ (tset forms ":" (fn [[obj field & args] scope] (.. (comp.expr obj scope) ":[" (scope:expr field) "](" (commasep args #(comp.expr $1 scope)) ")"))) +(fn forms.if [params scope] + (let [has-else? (= (% (length params) 2) 1) + ilast (if has-else? (length params) (- (length params) 1)) + ielse (when has-else? ilast) + clauses (fcollect [i 1 (length params) 2] + (if (= i ielse) + {:pre (indent :else scope) + :post :end + :clause (. params i)} + {:pre (.. (if (= i 1) :if (indent :elseif scope)) " " (comp.expr (. params i) scope) " then") + :post (if (= i ilast) :end "") + :clause (. params (+ i 1))})) + blocks (icollect [_ clause (ipairs clauses)] + (scope:with #(block clause.pre [clause.clause] clause.post scope #(comp.expr $1 scope))))] + (table.concat blocks "\n"))) + (fn comp.quote [stmts scope] (if (= (length stmts) 1) (.. "`(" (comp.expr (. stmts 1) scope) ")") (.. "quote\n" (commasep stmts #(comp.expr $1 scope)) "\nend")))