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.
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.
Sometimes you want to insert data from the stack or from a variable into a string. Template strings allow for this.
( BANANA )
( IS A )
<# this %# test and this is a %# fruit #>
.s
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">
</div>
</div>
</div>
To clear a div pass the id of that div to el:clear
/
( root ) el:clear
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
http:post
,
( 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