diff --git a/README.md b/README.md index 0c584e3..0855835 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,11 @@ Unlike Fennel, we do not implement implicit return semantics, and early returns Sorry Phil. ### q -Defines a terra quotation, compiling down to the ` `` ` operator if given one argument, and +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 @@ -125,7 +124,7 @@ a function call, but there is a shortened form using `$` to match the tuple inst #### 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 +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 @@ -136,22 +135,98 @@ 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 +(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. +#### assignment +```fennel +(set varname value) ; compiles to: varname = value +(set struct.field value) ; compiles to: struct.field = value +(tset struct (getfield) value) ; compiles to: struct.[getfield()] = value +``` + +#### field access +```fennel +struct.field ; compiles to: struct.field +(struct.func) ; compiles to: struct.func() +(obj:method) ; compiles to: obj:method() +(. struct (getfield)) ; compiles to: struct.[getfield()] +(: obj (getmethod)) ; compiles to: struct:[getmethod()]() +``` + +#### cast +```fennel +(cast type expr) +``` +Cast an expression `expr` to the type `type`. ```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 []) -``` \ No newline at end of file +(cast [int] voidptr) ; compiles to: ([&int]voidptr) +(cast [int8] (C.malloc (* (sizeof int8) 16))) ; compiles to: ([&int8]C.malloc(sizeof(int8) * 16)) +``` + +#### tuple literal +`$` can be used to create a tuple. + +```fennel +($ 5 2.5 :hello) ; compiles to: { 5, 2.5, "hello" } +(var pair ($ int int) ($ 5 10)) ; compiles to: var pair : { int, int } = { 5, 10 } +``` + +#### struct literal +A fennel key-value table literal is interpreted as an anonymous struct literal. +If you "call" a struct type with a struct literal, it will coerce it to the given +type. + +```fennel +(local Complex (ttype {real float imag float})) +; compiles to: +; local Complex = struct { real : float, imag : float } + +(def [: Complex] (return (Complex { real 5 imag 1 }))) +; compiles to: +; terra (): Complex +; return Complex({ real = 5, imag = 1 }) +; end +``` + +#### Primitive operators + +| Fennel | Terra | Meaning | +| ------------ | ------------ | ----------------------------------- | +| `(+ x y)` | `x + y` | add x and y | +| `(- x y)` | `x - y` | subtract y from x | +| `(/ x y)` | `x / y` | divide x by y | +| `(* x y)` | `x * y` | multiply x and y | +| `(% x y)` | `x % y` | x modulo y | +| `(< x y)` | `x < y` | x is less than y | +| `(<= x y)` | `x <= y` | x is less than or equal to y | +| `(= x y)` | `x == y` | x is equal to y | +| `(not= x y)` | `x ~= y` | x is not equal to y | +| `(> x y)` | `x > y` | x is greater than y | +| `(>= x y)` | `x >= y` | x is greater than or equal to y | +| `(and x y)` | `x and y` | x AND y (boolean or bitwise) | +| `(or x y)` | `x or y` | x OR y (boolean or bitwise) | +| `(not x)` | `not x` | NOT x (boolean or bitwise) | +| `(^ x y)` | `x ^ y` | x XOR y (bitwise) | +| `(<< x y)` | `x << y` | arithmetic shift x left by y bits | +| `(>> x y)` | `x >> y` | arithmetic shift x right by y bits | + +#### Fennel escaping diff --git a/go.fnl b/go.fnl index b838d04..45693ca 100644 --- a/go.fnl +++ b/go.fnl @@ -1,23 +1,18 @@ (local fennel (require :fennel)) (import-macros {: def : q : ttype : static : unterra : untype} :terra) -(local N (static int 5)) -(print N) -(fn inc [x] - (print "calling inc" x) - (q (+ ,x 1))) - +(local Complex (ttype {real float imag float})) (print (unterra - (def [x int] - (set N (+ N x)) - (return N)))) + (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))) -(local addN (def [x int] - (set N (+ N x)) - (return N))) - -(print addN) -(print (N:get) (addN 3) (N:get)) - -; (local inc (def [x [int]] (return [x N]))) -; (print (inc 5)) +(let [result (thing)] + (print result result.real result.imag (get-real result)) + (set-real result 10) + (print (get-real result))) diff --git a/terra.fnl b/terra.fnl index 6e91b8c..508e7c1 100644 --- a/terra.fnl +++ b/terra.fnl @@ -193,13 +193,23 @@ (fn def-infix [fnlop luaop] (tset forms fnlop (fn [[left right] scope] (.. "(" (comp.expr left scope) " " luaop " " (comp.expr right scope) ")")))) -(each [_ op (ipairs [:+ :- :* :/ :% :< :<= :> :>= :and :or :not :^ :<< :>>])] +(each [_ op (ipairs [:+ :- :* :/ :% :< :<= :> :>= :and :or :^ :<< :>>])] (def-infix op op)) (def-infix := :==) (def-infix :not= "~=") - +(fn forms.not [[expr] scope] (.. "not (" (comp.expr expr scope) ")")) (fn forms.set [[left right] scope] (.. (comp.expr left scope) " = (" (comp.expr right scope) ")")) +(fn forms.tset [args scope] + (let [iright (length args) + right (. args iright)] + (tset args iright nil) + (.. ((. forms ".") args scope) " = (" (comp.expr right scope) ")"))) + +(tset forms "." (fn [[struct & fields] scope] + (.. (comp.expr struct scope) (commasep fields #(.. ".[" (scope:expr $1) "]") "")))) +(tset forms ":" (fn [[obj field & args] scope] + (.. (comp.expr obj scope) ":[" (scope:expr field) "](" (commasep args #(comp.expr $1 scope)) ")"))) (fn comp.quote [stmts scope] (if (= (length stmts) 1) (.. "`(" (comp.expr (. stmts 1) scope) ")")