Undefined function defs and if statements

This commit is contained in:
Jeremy Penner 2023-12-04 20:40:52 -05:00
parent eafe21c6db
commit 7560aac500
3 changed files with 62 additions and 30 deletions

View file

@ -18,15 +18,15 @@ I could maybe be persuaded to make `def` work like `fn` and optionally define a
eh, whatever. eh, whatever.
### def ### 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 ```fennel
; syntax:
(def [argname1 argtype1 argname2 argtype2... : rettype1 rettype2...] statement...) (def [argname1 argtype1 argname2 argtype2... : rettype1 rettype2...] statement...)
``` (def [argtype1 argtype2... : rettype1 rettype2...])
Simple example: ; examples:
```fennel
(local add (def [x int y int : int] (local add (def [x int y int : int]
(return (+ x y)))) (return (+ x y))))
@ -36,10 +36,31 @@ Simple example:
; end ; end
(add 1 2) ; returns 3 (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 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. Unlike Fennel, we do not implement implicit return semantics, and early returns are A-OK.
Sorry Phil. Sorry Phil.

31
go.fnl
View file

@ -1,22 +1,15 @@
(local fennel (require :fennel)) (local fennel (require :fennel))
(import-macros {: def : q : ttype : static : unterra : untype} :terra) (import-macros {: def : q : ttype : static : unterra : untype} :terra)
(local Complex (ttype {real float imag float})) (local iseven (def [uint32 : bool]))
(print (unterra (local isodd (def [n uint32 : bool]
(def [: Complex] (return (Complex { real 5 imag 1 }))) (if (= n 0)
)) (return true)
(print (unterra (return (iseven (- n 1))))))
(def [c Complex r float] (tset c :real r)) (iseven:adddefinition (def [n uint32 : bool]
)) (if (= n 0)
(local thing (def [: Complex] (return (Complex { real 5 imag 1 })))) (return false)
(local get-real (def [val Complex] (return (. val :real)))) (return (isodd (- n 1))))))
(local set-real (def [c [Complex] r float] (tset c :real r))) (print iseven)
(print (iseven 5) (isodd 5))
(let [result (thing)] (print (iseven 6) (isodd 6))
(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))
))

View file

@ -184,11 +184,13 @@
(scope:with #(block :do stmts :end scope #(comp.expr $1 scope)))) (scope:with #(block :do stmts :end scope #(comp.expr $1 scope))))
(fn forms.def [[arglist & stmts] scope] (fn forms.def [[arglist & stmts] scope]
(if (> (length stmts) 0)
(scope:with #(let [(in out) (split-arglist arglist) (scope:with #(let [(in out) (split-arglist arglist)
argpairs (fcollect [i 1 (length in) 2] {:name (. in i) :type (. in (+ i 1))}) argpairs (fcollect [i 1 (length in) 2] {:name (. in i) :type (. in (+ i 1))})
rettyp (if out (.. ": { " (commasep out #(comp.type $1 scope)) " }") "") rettyp (if out (.. ": { " (commasep out #(comp.type $1 scope)) " }") "")
argdefs (commasep argpairs #(.. (scope:addlocal $1.name) " : " (comp.type $1.type scope)))] argdefs (commasep argpairs #(.. (scope:addlocal $1.name) " : " (comp.type $1.type scope)))]
(block (.. "terra (" argdefs ")" rettyp) stmts :end scope #(comp.expr $1 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)))) (fn forms.return [vals scope] (.. "return " (commasep vals #(comp.expr $1 scope))))
@ -220,6 +222,22 @@
(tset forms ":" (fn [[obj field & args] scope] (tset forms ":" (fn [[obj field & args] scope]
(.. (comp.expr obj scope) ":[" (scope:expr field) "](" (commasep args #(comp.expr $1 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] (fn comp.quote [stmts scope]
(if (= (length stmts) 1) (.. "`(" (comp.expr (. stmts 1) scope) ")") (if (= (length stmts) 1) (.. "`(" (comp.expr (. stmts 1) scope) ")")
(.. "quote\n" (commasep stmts #(comp.expr $1 scope)) "\nend"))) (.. "quote\n" (commasep stmts #(comp.expr $1 scope)) "\nend")))