guava/COOKBOOK.md -rw-r--r-- 7.1 KiB
446de4d1eli_oat Updated COOKBOOK 9 months ago

#Cooking with Guava

Here is a hopefully ever growing collection of Guava recipes that show off how to use the language and its features.


Guava uses C-style comments instead of the more traditional Forth-style ones.

/* this is a comment in Guava */


Strings of text must be surrounded in parentheses and spaces, e.g.

( this is a string )

Note the leading and trailing spaces after the first parentheses and before the last one. This is necessary because Guava, like more traditional Forth systems treats every space delimited word as a unique entity...mostly...Guava has some exceptions to this rule in sigils.

#Defining words

You can define new words (a word is sort of like a function in another language) using 1 of 2 available sigils.

:hi ( hello world ) . ;

This defines a new word, hi, which, when used pops the string hello world to the console.

Another way of writing this same word is like this,

[ ( hello world ) . ] \hi

There is no huge difference between these two under the hood.


Store data into a variable with the ! sigil. I call this "banging" data into a variable.

Get that data back out with the ? sigil. I call this asking for the data from a variable.

100 !number
?number 100 + .

The above stores the number 100 into a variable number, then retrieves the stored data to preform some math on it.

#Template strings

Sometimes you want to insert data from the stack or from a variable into a string. Template strings allow for this.

( IS A )
<# this %# test and this is a %# fruit #>

The above returns this IS A test and this is a BANANA fruit. First the strings BANANA and IS A are placed into the stack, next a template string is placed into the stack, including 2 filler spaces.

A template string is any string demarcated by the pair <# #>. Any text between the two appears as-is. Items from the stack are placed into the template string wherever a key is identified by the %# word.

Because Guava is implemented in JavaScript, and because of the weird way that JavaScript treats concatenation of strings, you can sort of accomplish the same thing using the arithmetic + operator.

( hello ) !h ( world ) !ws
?w space + ?h + .s 

That is a heap harder to read, though.


Guava ships with a few useful words for testing.

The assert word is useful for writing tests. Here is a failing test -- this will leave false on the stack.

[ 5 5 + ] [ 2 ] assert .

Here are 2 passing tests. Each will leave true on the stack.

[ 5 5 + ] [ 10 ] assert .
[ 5 5 eq? ] [ true ] assert .

Here is a shorter way of writing that last test using the assert:true word. There is also an assert:false word -- you can guess what it does.

[ 5 5 eq? ] assert:true .


Guava comes with a variety of words to add elements to the DOM.

These words are (loosely) inspired by jQuery's way of adding elements to the DOM.

All DOM words start with the prefix el:, for "element."

To add a new "root" <div> directly to the body of a document you can use the word, el:root. el:root takes in a string from the stack, and leaves nothing on the stack, instead adding a new div with the idea of the string to the browser document.

el:append takes in 2 strings from the stack, the first a target id for an existing div, the second the id for the new div to be appended to the browser document.

Next is el:html, this also takes in 2 string from the stack -- the first a target id of an existing div, the second html to insert into that div.

As well as el:html there are a variety of helper words to make building specific known structures a bit easier, including el:p, el:h1, el:h2, el:h3, and el:a.

( root ) el:root
( root ) ( child ) el:append 
( child ) ( <h1>Hello, world</h1> ) el:html 
( child ) ( <p>Sorta like jQuery.</p> ) el:html
( child ) ( grandchild ) el:append
( grandchild ) ( <img src="guava.png" alt="Guava Fruit"> ) el:html

Produces the following html markup,

<div id="root">
    <div id="child">
        <h1>Hello, world</h1>
        <p>Sorta like jQuery.</p>
        <div id="grandchild">
            <img src="guava.png" alt="Guava Fruit">

To clear a div pass the id of that div to el:clear/

( root ) el:clear

#HTTP requests

Guava comes with 2 naive words to make HTTP requests, http:get and http:post. Neither are particularly robust, and both will be made a bit more generic/powerful sometime in the near future, I hope.

Examples of http:get in action,

( https://httpbin.org/get ) [ dup . ( origin ) obj:parse . ] http:get

Will return the raw response object, duplicate it, then display just the origin value form the response. Or, to do something similar but output the origin to the DOM,

( https://httpbin.org/get ) [ dup . ( origin ) obj:parse !a ( root ) el:root ( root ) ?a el:html ] http:get

Another example that works in Guava's DOM stuff but doesn't barf to the console,

( https://httpbin.org/get ) [ dup ( origin ) obj:parse !resp ( playground ) ?resp el:html ] http:get
( https://httpbin.org/get ) [ dup ( headers ) obj:parse ( Accept-Language ) obj:parse !resp ( playground ) ?resp el:html ] http:get
( https://pokeapi.co/api/v2/pokemon/ditto ) [ ( name ) obj:parse !name ( playground ) ?name el:html ] http:get
( https://pokeapi.co/api/v2/pokemon/ditto ) [ ( name ) obj:parse !name ( playground ) ?name <# <h1> %# </h1> #> el:html ] http:get
( https://pokeapi.co/api/v2/pokemon/ditto ) [ dup ( name ) obj:parse !name ( sprites ) obj:parse ( front_default ) obj:parse !image ( playground ) ?name <# <h1> %# </h1> #> el:html ( playground ) ?image <# <img src=" %# "/> #> el:html ] http:get

Pokemon words!?

:pokedex ( https://pokeapi.co/api/v2/pokemon/ ) + [ dup ( name ) obj:parse !name ( sprites ) obj:parse ( front_default ) obj:parse !image ( playground ) ?name <# <h1> %# </h1> #> el:html ( playground ) ?image <# <img src=" %# "/> #> el:html ] http:get ;
( ditto ) pokedex
( 123 ) pokedex
( slowpoke ) pokedex


( https://httpbin.org/post ) ( "{"payload":1234,"fruit":"banana"}" ) [ dup . ( origin ) obj:parse . ] http:post


Guava has two words for loops, times and repeat.


Here, we use the times word to loop through each number 1 to 151 (inclusive).

First we bang the number 1 into the variable number. Then we ask for number to be put onto the stack so that it can be consumed by the pokedex word. Then number is asked for again, and incremented by 1, and re-assigned into number...repeat 151 times.

1 !number
[ ?number pokedex ?number ++ !number ] 151 times

This will execute the body 151 times.

times is essentially a for loop like so,

for (let i = 0; i < n; i++) {
    // do stuff


repeat is similar to times, but, whereas times is like a for loop, repeat is more like a while loop, as such, repeat needs to be paired with either while or until.

1 !a [ ?a .s cr ?a ++ !a ] while [ ?a 5 lteq? ] repeat
1 !a [ ?a .s cr ?a ++ !a ] until [ ?a 5 lteq? ] repeat