3.7 KiB
Runtime
Any ginger data-structure can be put into a ginger file. The data itself has no executional meaning on its own, but if
the data is properly formed it can be parsed by the ginger interpreter and a thread of execution can be started. For
example, if I put the following in a file called test.gng
:
(1 2 3)
It doesn't have any meaning, it's just a list. However, if you put the following:
(add 1 2 3)
and run ginger test.gng
then a program can be interpreted from the given data. This file describes how given data can
be formed into a valid program.
Eval
Ginger evaluation is done the same was as other lisp languages: the first item in a list is the function name, the rest
of the items in the list are arguments to the function. In the example above, add
is the function name, and 1
, 2
,
and 3
are arguments to the function.
Arguments to a function can be functions to be eval'd themselves. An equivalent to the example above would have been:
(add 1 2 (add 1 2))
Doing multiple things
It's not very useful to only be able to do one thing. A vector of lists is interpreted into sequentially eval'ing each item in the vector. For (a trivial) example:
[ (add 1 2)
(sub 4 (add 1 2))
(mul 8 0) ]
Variables/Scope
The above example does a few things, but it repeats itself in the second part (with the sub
). If we could save the
result of the addition to a variable that would be awesomesauce. The =
function does this! It makes it so that the
first argument is equivalent to the evaluation of the second argument. Variables' first letters must be upper-case, this
is how they're differentiated from raw string literals. The above could be re-written as:
[ (= AdditionResult (add 1 2))
(sub 4 AdditionResult)
(mul 8 0) ]
In the above example AdditionResult
is a valid variable inside of the vector that contains its declaration, and any
vectors contained within that vector. For example:
[ (= AdditionResult (add 1 2))
[ (sub 4 AdditionResult) ;This works
(mul 8 0) ] ]
[ (add 4 AdditionResult) ] ;This does not work!
Literals
We've determined that a list is interpreted as a function/arguments set, and an upper-case string is interpreted as a
variable name which will be de-referenced. What if we want to actually use these structures without eval'ing them? For
these cases we have the literal function l
:
[ (concat (l (1 2 3)) (l (4 5 6))) ; => (1 2 3 4 5 6)
(println (l "I start with a capital letter and I DONT CARE!!!")) ]
Functions
Anonymous
Anonymous functions are declared using the fn
function. The first argument is a vector of argument names (remember,
all upper-case!) and the second is a list/vector to be eval'd. Examples:
[ (= Add3 (fn [Num] (add Num 3)))
(Add3 4) ; => 7
(= Add3Sub1
(fn [Num] [
(= Added (add Num 3))
(sub Added 1)
]))
(Add3Sub1 4) ] ; => 6
fn
returns a function, which can be passed around and assigned like any other value. In the above examples the
functions are assigned to the Add3
and Add3Sub1
variables. Functions can also be passed into other functions as
arguments:
;DoTwice takes a function Fun and a number Num. it will call Fun twice on Num and return the result.
[ (= DoTwice
(fn [Fun Num]
(Fun (Fun Num))))
(DoTwice (fn [Num] (add Num 1)) 3) ] ; => 5
Defined
Defined functions attach the function definition to a string literal (lower-case) in the current scope. They are useful
as they support more features then an anonymous function, such as inline documentation. These extra features will be
documented elsewhere. To create a defined function use the dfn
function:
[ (dfn add-four-sub-three [Num] [
(= A (add Num 4))
(sub A 3) ])
(add-four-sub-three 4) ] ; => 5