ginger/doc/runtime.md

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