2021-06-19 20:58:54 +02:00
# FCL - Forth Calculator's Language
2021-07-03 12:22:39 +02:00
FCL is the programming language of an Android app called [Forth Calculator ](https://play.google.com/store/apps/details?id=com.vectron.forthcalc.pro ). It is a Forth dialect with optional local variables, complex data structures, quotations and Java interoperability.
2021-06-19 20:58:54 +02:00
2021-07-02 16:56:24 +02:00
< img align = "right" src = "/img/screenshot1.png" width = "180" >
2021-07-02 16:52:24 +02:00
2021-06-30 09:41:56 +02:00
Watch this [demo ](https://www.youtube.com/watch?v=9rf8Y_lwj3g ) to see it in action.
2021-06-19 20:58:54 +02:00
```forth
2021-06-27 16:58:34 +02:00
\ calculates the first n+2 elements of the fibonacci sequence
2021-06-20 11:12:48 +02:00
: fib ( n -- n* ) -> n ( local variable )
0 1 { 2dup + } n times ; ( quotation )
2021-06-19 20:58:54 +02:00
```
2021-06-19 21:08:32 +02:00
Besides all the high-level features, FCL supports the traditional Forth programming structures and uses the same compilation model (compile/interpret mode, dictionary, immediate words, etc.) as traditional Forth systems.
## The Syntax
2021-06-24 23:09:57 +02:00
The syntax is a superset of the Forth language. In FCL there are literal syntax for creaing Lists `[ 1 2 3 ]` , Maps `#[ 'key' 'value' ]#` , Quotations `{ dup + }` , Strings `'Hello World'` , and Ranges `1 10 ..` . But many other things are the same as in a traditional Forth system.
2021-06-19 21:21:35 +02:00
2021-06-20 00:12:10 +02:00
## Low-level control structures
2021-06-19 21:21:35 +02:00
2021-06-21 23:22:53 +02:00
FCL supports the traditional Forth conditionals (`if` and `case` ) and loops (`do`, `while` , `until` ). These are immediate words whose compilation semantics are to append the proper JUMP primitives to the current definition. FCL compiles high level threaded code, where execution tokens are method references of host language (Java).
2021-06-19 21:33:31 +02:00
General form of `if else then` .
```forth
< bool > if < consequent > else < alternative > then
```
For example:
```forth
2021-06-20 13:32:53 +02:00
: max ( n n -- n )
2021-06-19 21:33:31 +02:00
2dup < if nip else drop then ;
10 100 max . \ prints 100
```
2021-06-19 21:34:21 +02:00
The `else` part is optional.
2021-06-19 21:33:31 +02:00
```forth
2021-06-19 21:35:24 +02:00
: abs ( n -- n )
dup 0 < if -1 * then ;
2021-06-19 21:33:31 +02:00
-10 abs . \ prints 10
```
#### Case statement
FCL supports switch-case like flow control logic as shown in the following example.
```forth
2021-06-20 13:32:53 +02:00
: day ( n -- s )
2021-06-19 21:33:31 +02:00
case
2021-06-20 13:32:53 +02:00
1 of 'Monday' endof
2 of 'Tuesday' endof
3 of 'Wednesday' endof
4 of 'Thursday' endof
5 of 'Friday' endof
6 of 'Saturday' endof
7 of 'Sunday' endof
2021-06-19 21:33:31 +02:00
drop 'Unknown'
endcase ;
````
#### Count-controlled loops
The `limit` and `start` before the word `do` defines the number of times the loop will run.
```forth
< limit > < start > do < loop-body > loop
```
2021-06-20 15:43:21 +02:00
*DO* loops iterate through integers by starting at *start* and incrementing until you reach the *limit* . The word *i* pushes the loop index onto the stack. In a nested loop, the inner loop may access the loop variable of the outer loop by using the word *j* .
2021-06-19 21:33:31 +02:00
For example:
```forth
5 0 do i . loop \ prints 0 1 2 3 4
```
It is important to understand the implementation details of this loop. `DO` loops store the loop index on the return stack. You can break the semantics of *i* and *j* if you use the return stack to store temporary data. Exiting from the loop requires clearing up the return stack by using the `unloop` word.
#### Condition-controlled loops
##### until loop
```forth
begin < loop-body > < bool > until
```
The *begin* ...*until* loop repeats until a condition is true. This loop always executes at least one time.
For example:
```forth
: countdown ( n -- )
begin
2021-06-20 13:33:59 +02:00
dup .
1-
dup 0 <
2021-06-19 21:36:46 +02:00
until
2021-06-19 21:33:31 +02:00
drop ;
5 countdown \ prints 5 4 3 2 1 0
```
##### while loop
```forth
2021-06-20 01:03:39 +02:00
begin < bool-exp > while < loop-body > repeat
2021-06-19 21:33:31 +02:00
```
For example:
```forth
: countdown ( n -- )
begin
dup 0 >=
while
dup . 1-
repeat
drop ;
5 countdown \ prints 5 4 3 2 1 0
```
Control structres are compile time words with no interpretation semantics.
2021-06-19 21:08:32 +02:00
## Locals
2021-06-19 21:43:27 +02:00
```
: example ( a b -- n )
-> b -> a 42 -> c 0 => d
a b + c * d !
d @ ;
```
2021-06-21 22:44:14 +02:00
You can load the top of the stack to a local by either using `->` or `=>` . The name after the arrow denotes the name of the local and its value comes from the data stack.
2021-06-19 21:43:27 +02:00
There are two types of locals in FCL. Local constant `->` and local variable `=>` .
2021-06-19 21:49:40 +02:00
`-> a` loads the top of the stack into the local, called `a` .
2021-06-19 21:43:27 +02:00
2021-06-21 22:44:14 +02:00
Using `a` anywhere inside the word will push the value of the local.
2021-06-19 21:43:27 +02:00
2021-06-19 21:49:40 +02:00
`=> b` loads the top of the stack into the local variable, called `b` .
2021-06-19 21:43:27 +02:00
2021-06-21 22:44:14 +02:00
`b` pushes the address of the local. `b @` pushes the value of the local. You can use the `!` word to change the value of the local variable.
2021-06-19 21:49:40 +02:00
The `->` and `=>` words can be used anywhere within a word, including loop bodies and quotations. You can initialize a local (`0 -> a`) within the word or use the data that was supplied on the call site (`-> a`).
2021-06-21 22:44:14 +02:00
The locals are only accessible by the current word or a quotation which was defined within the word.
For example, here we load first parameter into `n` and initialize a local varialbe, called `count` to zero.
2021-06-19 21:49:40 +02:00
```
: count-even ( n -- c )
-> n 0 => count
n 0 do
i 2 /mod -> quotient -> remainder
remainder 0 = if
count inc
then
loop
count @ ;
```
2021-06-19 21:08:32 +02:00
2021-06-21 23:27:28 +02:00
In the loop body we use two more locals to name the output of the `/mod` which returns both the `quotient` and the `remainder` of a divide operation. We keep updating the `count` and in the end, we return its value.
2021-06-21 22:44:14 +02:00
2021-06-19 22:02:49 +02:00
### Implementation notes
2021-06-25 15:07:37 +02:00
Local variable support is [implemented ](src/main/res/raw/locals.forth ) in FCL itself. Locals are stored in a parameter stack. Both `->` and `=>` are immediate parsing words. They have both runtime and compilation semantics. They compile an inline *lookup word* within the enclosing word. The lookup word is removed from the dictionary after the compilation is finished.
2021-06-21 22:44:14 +02:00
At runtime they load the top of the stack into the proper location of the parameter stack which is associated to the current local.
The *lookup word* gets the value (or the address) from the parameter stack and pushes it onto the data stack. The `exit` and `;` words are redefined so that they unwind the parameter stack at return.
2021-06-19 23:58:24 +02:00
## Quotations
```forth
2021-06-20 11:06:41 +02:00
{ dup * } \ creates a quotation
2021-06-19 23:58:24 +02:00
```
2021-06-21 22:44:14 +02:00
A quotations is an anonymous word that contain a snippet of code and its evaluation is delayed until it is called by the word `yield` .
2021-06-19 23:58:24 +02:00
```forth
2021-06-21 22:44:14 +02:00
{ 'hello world' . } \ quotation pushes its address (plus a parameter stack adddress) to the data stack
2021-06-19 23:58:24 +02:00
yield \ calls the quotation
```
```forth
{ 'hello' . } 10 times
```
A quotation can access to local variables of the enclosing word and have its own local variables as well.
```forth
: tst ( -- n )
0 => sum
[ 1 2 3 4 5 ] { -> item sum @ item + sum ! } each
sum @ ;
```
2021-06-21 22:44:14 +02:00
Here the quotation is called by the word `each` not by `tst` . It can still access to `sum` as it maintains the same stack frame as `tst` .
2021-06-20 15:43:21 +02:00
Local variables are lexically scoped. If the quotation is called by another word, the `sum` still denotes the variable that was defined in the quotation's context.
2021-06-19 23:58:24 +02:00
Quotations don't act as lexical closures however. The parameter stack is unwinded after the enclosing function is returned.
### Implementation notes
2021-06-25 15:09:17 +02:00
The quotation code is compiled into the enclosing word and bypassed by a jump. At runtime the quotation pushes its address as well as a stack frame to the stack. The word `yield` calls the address like a normal word and sets the parameter stack pointer to point to the quotation's stack frame. Quotations are [implemented ](src/main/res/raw/quotations.forth ) in fcl.
2021-06-19 22:02:49 +02:00
2021-06-21 23:00:46 +02:00
## Strings
2021-06-21 23:16:43 +02:00
Strings are surrounded by single quotes and they're immutable. For example `'Hello world'` .
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'hello world' 0 5 substr \ gets the characters from 0 to 5 (exclusive)
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'hello world' 1 at \ gets the first character (as a string)
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'hello' upper \ gets the upper case version of hello
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'HELLO' lower \ gets the lower case version of hello
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
' xx ' trim \ removes the leading and trailing spaces from the string
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'abcd' 'bc' index-of \ finds the substring and returns its index, or -1 if not found
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'abcxxdepkcxxk' 'xx' 'yyy' replace \ replace all occurances of 'xx' to 'yyy (the substring is a regexp)
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'af' '123' concat \ concatenates two strings
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'hello world' ' ' split \ splits the strings into parts by the separator string
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
123 >str \ conversts the number into a string
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
"[ 1 'xx' ] 'a=%d b=%s' format" \ creates a string using the given format by substituting the parts in place of the format characters.
```
2021-06-24 23:12:05 +02:00
```forth
2021-06-21 23:16:43 +02:00
'ab' 3 * \ copies the string n times
```
2021-06-21 23:00:46 +02:00
2021-06-19 21:08:32 +02:00
## List
2021-06-21 23:20:02 +02:00
A list is a dynamic, ordered data structed. `[` and `]` are Forth words, so a whitespace between them and the elements are significant.
2021-06-19 22:02:49 +02:00
2021-06-21 22:53:33 +02:00
```forth
2021-06-21 22:54:57 +02:00
< list > \ creates a new empty list
```
2021-06-19 22:02:49 +02:00
2021-06-21 22:54:57 +02:00
```forth
< list > dup 1 add \ creates an empty list and adds 1 to it
```
2021-06-19 22:02:49 +02:00
2021-06-21 22:54:57 +02:00
```forth
[ 1 2 3 ] \ creates a list with 3 elements
```
2021-06-21 22:51:53 +02:00
2021-06-21 22:54:57 +02:00
```forth
[ 1 2 3 ] peel \ unloads the items from the list to the data stack
```
2021-06-21 22:51:53 +02:00
2021-06-21 22:54:57 +02:00
```forth
2021-06-24 12:15:16 +02:00
1 2 3 4 5 list* \ creates a new list and loads all items from the stack into it
2021-06-21 22:54:57 +02:00
```
2021-06-21 22:51:53 +02:00
2021-06-21 22:54:57 +02:00
```forth
[ 1 2 3 ] 0 at \ returns the first item of the list
```
2021-06-21 22:51:53 +02:00
2021-06-21 22:54:57 +02:00
```forth
[ 1 2 3 ] 0 remove-at \ removes the first item from the list
```
2021-06-21 22:51:53 +02:00
2021-06-21 22:54:57 +02:00
```forth
2021-06-21 22:56:34 +02:00
[ 1 'abc' 3 ] 'abc` remove \ removes 'abc' from the list
2021-06-21 22:54:57 +02:00
```
2021-06-21 22:51:53 +02:00
2021-06-21 22:54:57 +02:00
```forth
[ 1 'abc' 3 ] index-of \ returns the index of 'abc'
```
2021-06-21 22:51:53 +02:00
2021-06-21 22:54:57 +02:00
```forth
[ 1 2 ] [ 3 4 ] concat \ creates a new list with the first concatenated to the second
```
2021-06-21 22:51:53 +02:00
2021-06-21 22:54:57 +02:00
```forth
[ 1 2 3 4 ] 1 3 sublst \ gets a sublist from the original from 1 (inclusive) to 3 (exclusive)
```
2021-06-21 22:51:53 +02:00
2021-06-21 23:20:02 +02:00
```forth
[ 1 2 3 ] 2 * \ basic arithmetic with scalars work with lists, this will multiple each item with 2.
2021-06-21 23:18:40 +02:00
```
2021-06-21 22:51:53 +02:00
### Implementation notes
2021-06-19 22:02:49 +02:00
2021-06-20 00:04:35 +02:00
Lists are java.util.ArrayList instances and garbage collected automatically by the host language.
2021-06-19 22:02:49 +02:00
2021-06-19 21:43:27 +02:00
## Maps
2021-06-20 00:04:35 +02:00
Maps contain key value pairs.
2021-06-19 22:02:49 +02:00
2021-06-21 23:00:46 +02:00
```forth
< map > \ creates a new empty map
```
2021-06-19 22:02:49 +02:00
2021-06-21 23:00:46 +02:00
```forth
< map > dup 'key1' 'value1' put \ creates an empty map and puts *'key1' => 'value1'* into it.
```
```forth
2021-06-24 12:15:16 +02:00
'key1' 'value1' map* \ creates a new map and loads all items from the stack into it. There must be an even number of items on the stack.
2021-06-21 23:00:46 +02:00
```
```forth
2021-06-24 12:51:24 +02:00
#[ 'a' 1 'b' 2 ]# peel# \ unloads all items from the map to the stack
2021-06-21 23:00:46 +02:00
```
2021-06-19 22:02:49 +02:00
2021-06-21 23:00:46 +02:00
```forth
#[ 'key1' 'value1' ]# \ creates a map with key and value
```
```forth
#[ 'a' 1 'b' 2 ]# keys \ returns the keys from the map
```
```forth
#[ 'a' 1 'b' 2 ]# values \ returns the values from the map
```
```forth
#[ 'a' 1 'b' 2 ]# 'b' remove \ removes 'b'->2 from the map
```
2021-06-19 22:02:49 +02:00
2021-06-21 23:01:26 +02:00
## Implementation notes
2021-06-20 00:04:35 +02:00
Lists are java.util.LinkedHashMap instances and garbage collected automatically by the host language.
## Collection operations
```forth
2021-06-20 00:05:21 +02:00
\ iterets through the list and calls the quotation on each element
[ 1 2 3 4 ] { . } each
2021-06-20 00:04:35 +02:00
```
```forth
2021-06-20 00:05:21 +02:00
\ selects only the odd items from the list [ 1 3 ]
[ 1 2 3 4 ] { odd? } filter
2021-06-20 00:04:35 +02:00
```
```forth
2021-06-20 00:05:21 +02:00
\ transforms the list to a new list that contain the squares of the original items [ 1 4 9 16 ]
[ 1 2 3 4 ] { dup * } map
2021-06-20 00:04:35 +02:00
```
```forth
2021-06-20 00:57:32 +02:00
1 5 .. \ creates a range from 1 to 5
2021-06-20 00:04:35 +02:00
```
2021-06-19 22:02:49 +02:00
2021-06-20 01:02:09 +02:00
```forth
10 1 -2 ... \ creates a range from 10 downto 2 by 2
```
2021-06-20 00:09:53 +02:00
```forth
\ adds 5 to the end of the list
[ 1 2 3 4 ] dup 5 add
```
```forth
\ prepends 0 to the beginning of the list
[ 1 2 3 4 ] dup 0 prep
```
```forth
[ 1 2 3 ] size \ gets the size of the list
```
```forth
[ 1 2 3 4 ] 2 at \ gets the second item of the list ( indexing is zero based )
```
```forth
alist clear \ removes all items from the list
```
2021-06-20 01:12:05 +02:00
## High level control structures
Quotations combined with the collection API offers some high level control structures.
```forth
2021-06-28 15:20:03 +02:00
1 10 .. { dup * . } each
2021-06-20 01:12:05 +02:00
```
2021-06-20 00:09:53 +02:00
2021-06-19 21:08:32 +02:00
## HTTP
2021-06-27 18:07:30 +02:00
2021-06-28 12:34:36 +02:00
There is basic support getting an url or posting a form.
```forth
2021-07-01 12:10:20 +02:00
http-get ( url -- response-body http-code )
2021-06-28 12:35:01 +02:00
http-post ( map url -- response-body http-code )
2021-07-01 12:10:20 +02:00
http-put ( map url -- response-body http-code )
2021-06-28 12:34:36 +02:00
```
For example:
```forth
'http://some.url' http-get
#[ 'year' year
'month' month
'day' day
'cost' 1234 ]#
'http://myhost.site/expense' http-post
```
2021-07-01 12:10:56 +02:00
You can also specify HTTP headers by adding a `headers` key to the map. In this case you should put the body under the `content` key.
2021-07-01 11:57:37 +02:00
```forth
#[
2021-07-01 12:07:38 +02:00
'headers' #[ < key1 > < value2 > < key2 > < value2 > ]#
2021-07-01 11:57:37 +02:00
'content' #[ .. ]#
]#
```
2021-07-01 12:07:38 +02:00
For example this will set the content type header to json and the body will be sent as a JSON object.
```forth
#[
'headers' #[ 'Content-Type' 'application/json' ]
'content' #[ 'on' true 'xy' [ 0.3 0.4 ] 'transitiontime' 10 ]#
]#
```
A shorter way to do the same is to use `+json-type` on the map.
```forth
#[ 'on' true 'xy' [ 0.3 0.4 ] 'transitiontime' 10 ]# +json-type
```
2021-07-01 11:54:11 +02:00
### Examples
2021-06-27 18:07:30 +02:00
2021-07-01 12:07:38 +02:00
#### Controlling Philips Hue lights
```forth
: hue ( x y msec -- )
2021-07-01 12:08:26 +02:00
10 / round -> t -> y -> x
2021-07-01 12:07:38 +02:00
#[
'on' true
'xy' [ x y ]
'bri' 254
'transitiontime' t ( centiseconds )
2021-07-01 12:09:06 +02:00
]#
2021-07-01 12:09:43 +02:00
+json-type 'http://< bridgeip > /api/< apikey > /groups/1/action' http-put ;
2021-07-01 12:07:38 +02:00
```
2021-07-01 11:54:11 +02:00
#### Controlling a Daikin Air Conditioner
2021-06-27 18:07:30 +02:00
```forth
2021-06-27 18:09:43 +02:00
: cool ( temperature -- response http-code )
2021-06-27 18:07:30 +02:00
-> tp
#[
'pow' tp 0 = if 0 else 1 then
'f_rate' 'A' ( auto fan )
'stemp' tp 24 min 20 max
'mode' 3 ( cooling )
'f_dir' 0 ( fan direction )
'shum' 0
]#
'http://192.168.0.25/aircon/set_control_info'
2021-07-01 12:09:43 +02:00
http-post ;
2021-06-27 18:07:30 +02:00
```
2021-06-27 18:09:43 +02:00
```forth
: ac? ( -- map )
< map > -> out
2021-06-27 18:12:27 +02:00
'http://192.168.0.25/aircon/get_control_info' http-get
dup 200 = if
2021-06-27 18:11:55 +02:00
drop ',' split
2021-06-27 18:13:20 +02:00
{ '=' split dup size 2 = if out swap add else drop then }
each
2021-06-27 18:09:43 +02:00
out
then ;
```
2021-07-01 11:54:11 +02:00
## Misc
### Tone generator
2021-07-01 11:54:28 +02:00
```forth
2021-07-01 11:54:11 +02:00
tone ( hz ms -- )
```
The `tone` word is for generating a tone with a given frequency and duration.