Thune Scripting System

1   Overview

Thune is an experimental interpreted language mixing ideas from Forth and Rebol.

Language features include:

  • Stack based evaluator.
  • Garbage collected datatype system with prototype based objects.
  • Written in C to work well as an embedded scripting language.
  • Small (but not tiny) binary & run-time environment.

1.1   About This Document

This manual is largely incomplete. It can only serve as a quick function reference at this point.

2   Scripts

2.1   Comments

Single line comments begin with a semi-colon.

Block comments are the same as C block comments. They begin with '/*' and continue through '*/'. Unlike C, block comments can be nested.

Comment examples:

; line comment

2 4 add  ; result is 6

/*
  Block comment
*/

3   Data Stack

Thune has a data stack which may be manipulated with many typical Forth operations. However, the stack stores 16-byte cells, similar to Rebol values, rather than register sized words.

4   Datatypes

Datatype Examples
unset!  
none! none
datatype! logic! int!/decimal!
logic! true false
word! hello big-time .s
lit-word! 'hello 'big-time '.s
set-word! :hello :big-time :.s
get-word! hello: big-time: .s:
block! [] [a b c]
paren! () (a b c)
macro! ^(size 2 mul)
vector! #[1 2 3] #[-85.33 2 44.8]
function! [2 add] proc :inc2
call!  
int! 1 455 -22
decimal! 3.05 -4.
coord! 0,255,100 -1, 0, 0
vec3! 0.0,255.0,100.0 -1.0, 0, 0
char! 'a' '^-'
string! "hello" {hello}
binary! #{01afed} #{00 33 ff a0}
select! obj/x my-block/2
set-select! :obj/x :my-block/2
lit-select! 'obj/x 'my-block/2
time! 10:02 -0:0:32.08
context!  
component!  

4.1   Macros

Reader macros are simply blocks of code evaluated by the tokenizer. Any reduced values created by the evaluation are appended to the block currently being read.

Macros can be used wherever a constant is needed to improve evaluation speed. They are also useful for conditional code generation.

Example:

^(34 2 mul :border)

box-width ^(border) add

Conditional Example:

^(script-env/os [
    linux   ["Linux"]
    solaris ["Solaris"]
    ["Unsupported OS" error]
 ] case)
print

Currently, macro! values cannot be created by the user. A literal macro syntax may be added in the future.

4.2   Binary

A binary value references a series of bytes.

#{0000ff01}
#{0000 ff01}
#{68656C6C6F}     ; "hello" as binary

4.3   Strings

Multi-line strings are contained within curly braces.

String examples:

"alpha"

{This string
spans multiple lines.}

4.4   Vector

Vectors hold a series of numbers using less memory than a block!.

All numbers in a vector are either 32-bit integers or floating point values. If any number is specified as a decimal!, all numbers will be floating point.

4.5   Coord

Integer coordinate that is handy for specifying screen coordinates, colors, IP-4 network address and such.

A coord! can hold up to 6 16-bit integers.

640,480       ; screen coordinate
255,10,0      ; rgb triplet
192,168,0,1   ; IP-4 network address

4.6   Vec3

Vec3 stores 3 floating point values.

A Vec3 is specified as two or three decimal numbers separated by commas. If none of the numbers has a decimal point then the value will be a coord!.

0.0, 1.0     ; Third component will be 0.0
1.0,0,100

4.7   Functions

Functions can be defined in two ways: as procedures or as functions.

Procedures are simply blocks of code which can be evaluated. Any number of values may be removed from or pushed onto the stack by a procedure.

; Here is a procedure
["Hello World" print] proc :hello

Functions are blocks of code bound to local arguments and variables, and may return either one value or no value. Function arguments are taken from the stack and local variables are initialized to 'none. These local values are declared in a signature block. Functions cannot access values on the stack placed there before the function was invoked. When a function reaches the end or returns, the top stack value (if any has been put there) will be copied to the stack position at the top before the function call (minus any arguments).

; Here is a function with two arguments and one local variable.
[arg1 arg2 | var1 -- ret]
[
    ; arg1 & arg2 hold what were the top two items on the stack.
    ; var1 is none.

    ; TODO: Write this function body.
] func :my-function

Function argument types can be limited by following the argument name with a datatype in the signature block.

[blk block!  count int!/decimal! -- "Argument type example"]
[
    ; ...
]
func

4.8   Contexts

context! [
  "John"  :name
  44      :age
  'farmer :job
] make :my-object

4.9   Components

Components are similar to functions, but they operate via a dataflow model. They have inputs, outputs which are connected to the inputs of other components, and a body which is evaluated when all inputs are set.

Components must have at least one input, but they don't require any outputs.

Here is an example of how to create and connect components:

[a b -- out] [a b mul :out] component :c1
[val] [val .] component :probe
'c1/out 'probe/val connect

Now, when the inputs to c1 are set, probe will automatically be invoked:

3 :c1/a
5 :c1/b
15

Further changes to the inputs will continue to invoke the probe component:

5 :c1/a
25

Connect also accepts components as arguments. This is short-hand for referring to the first input or output. The following call makes the same connection as the example above:

c1 probe connect

4.9.1   Quiet Inputs

Inputs can be declared with a /quiet selector to prevent them from activating the component when set.

Here is an example component with an enable switch:

[in enable/quiet -- out] [enable ift (in 2 mul :out)] component :mul2

false :mul2/enable    ; No activation
2 :mul2/in            ; Activation, but no output
true  :mul2/enable    ; No activation
2 :mul2/in            ; Activation with output

4.9.2   Strobe Inputs

A single input can be declared with a /strobe selector. This input will then control when the component activates. No other input will cause an activation.

5   Defined Words

The following abbreviations are used:

Abbreviation Meaning
TOS Top of data stack
EOL End of line

5.1   Data Stack

Word Stack Usage Function
. (val -- ) Remove value on top of stack and print it.
.s () Show values on stack.
dup (a -- a a) Duplicate value on top of stack.
dup2 (a b -- a b a b) Duplicate top 2 values.
drop (a -- ) Remove value on top of stack.
swap (a b -- b a) Switch the two values on the top of stack.
over (a b -- a b a) Copy second item on stack to the top.
nip (a b -- b) Remove second value on stack.
tuck (a b -- b a b) Copy top value under second value.
rot (a b c -- b c a) Rotate third value to top.
rot.r (a b c -- c a b) Rotate top of stack to third position.
stack.level ( -- level) Returns depth of data stack.

5.1.1   Data Stack Helpers

Word Stack Usage Function
.t (val -- val) Show value on top of stack.

5.2   Flow Control

Word Stack Usage Function
ift (logic -- ) Evaluate next value if true.
iff (logic -- ) Evaluate next value if false.
if/C (val val -- ) Evaluate next value if comparison is true.
if-some (val -- [val]) Evaluate next value if true, else drop.
or-else (val -- [val]) Keep true value, or drop and evaluate next.
either (logic t-val f-val -- ) Evaluate either t-val or f-val.
forever (block -- ) Evaluate block until exception thrown.
loop (block n -- ) Repeat block n times.
loop (block n limit -- ) Repeat block n to limit times.
iter (ser body -- ) Iterate over series.
iter/N (ser body -- ) Iterate over series with skip.
each (ser body -- ) Iterate over series elements.
each/N (ser body -- ) Iterate over series elements with skip.
proc (body -- proc) Create procedure.
func (sig body -- func) Create function with local values.
return () Leave function.
recurse () Start function from beginning.
do (a -- ) Evaluate.
select (data options -- match) Choose option which matches data.
case (data options -- ) Evaluate option which matches data.
throw (val -- val) Throw exception.
try (body catch -- ) Try body and catch exception.
break ( -- 'break) Break from forever & iter.
error (message -- error) Throw error.
verify (a type -- a) Throw error if datatype does not match.
verify/N (a1-aN t1-tN -- a1-aN) Throw error if datatypes do not match.
quit () Quit program

5.2.1   Flow Helpers

Word Stack Usage Function
each.set (ser words body -- ) Iterate over series and assign elements.
while (body cond -- ) Evaluate body while cond is true.
loop.to (block n limit -- ) Repeat block n to limit times.
proc.env (env body -- proc) Create procedure with private context.
func.env (env sig body -- func) Create function with private context.
q () Quit

5.3   Data Manipulation

Word Stack Usage Function
make (type proto [p2] -- val) Create value.
as (val type -- val) View value as a similar type.
to (val type -- val) Convert value to a similar type.
type? (val -- type) Replace value with datatype of value.
type?.word (val -- word) Replace value with datatype word.
is-type? (val type -- logic) True if value is of specified type.
set (val word -- ) Assign value to word.
set (val word ctx -- ) Add word to context.
get (word -- val) Retrieve value referenced by word.
get (word ctx -- val) Retrieve value from context.
bind (block ctx -- block) Bind block to context.
infuse (block ctx -- block) Replace words with context values.
lift-local (block -- block) Rebind words to copy of local context.
reduce (val -- rval) Replace with evaluated values.
copy (a -- r) Copy value.
copy.part (ser limit -- r) Copy part of a series.
mark.sol (val -- block) Set Start Of Line flag for current cell.
parse (data rules -- pos) Parse string or block.

5.3.1   Data Helpers

Word Stack Usage Function
context (def -- context) Create context.
parse.some (data rules -- ) Parse string or block.

5.4   Series

Word Stack Usage Function
append (ser val -- ser) Append value to series.
append (bin num bytes -- bin) Append number to binary.
append.cat (ser val -- ser) Join value to series.
insert (ser val -- ser) Insert value into series.
remove (ser -- ser) Remove value at current series position.
fill (ser limit val -- ser) Fill series with value.
clear (ser -- ser) Remove all elements.
head (ser -- ser) Move position to series start.
tail (ser -- ser) Move position just past series end.
head? (ser -- logic) True if series is at the start.
tail? (ser -- logic) True if series is past the end.
index? (ser -- int) Position in series.
length? (ser -- int) Length of series.
prev (ser -- ser) Decrement start of series.
next (ser -- ser) Increment start of series.
skip (ser n -- ser) Increment start of series by n.
slice (ser end -- slice) Slice series.
slice? (ser end -- slice) True if series is a slice.
slice.prev (slice -- slice) Decrement end of slice.
slice.next (slice -- slice) Increment end of slice.
pick (ser index -- val) Fetch an element from a series.
poke (ser val index -- ser) Set an element of a series.
push (ser val -- ) Append value to series and drop.
pop (ser -- ) Remove last element from series and drop.
first (ser -- val) Fetch first value in series.
second (ser -- val) Fetch second value in series.
third (ser -- val) Fetch third value in series.
last (ser -- val) Fetch last value in series.
change (ser new -- ser) Change series.
change (ser new part -- ser) Change part of series.
find (ser pat -- ser) Find pattern in series.
find.last (ser pat -- ser) Find pattern in series starting from end.
match (ser pat -- end) Move position to end of matching pattern.
trim (str -- str) Remove white space from start and end.

5.4.1   Series Helpers

Word Stack Usage Function
empty? (ser -- logic) Same as tail?.
appen (ser val -- ) Append and drop.
map (ser comb -- ) Transform each element of a series.
replace.all (ser old new -- ser) Find & Replace

5.5   Math

Word Stack Usage Function
inc (num -- num) Increment number
dec (num -- num) Decrement number
inc (word -- ) Increment number
dec (word -- ) Decrement number
add (a b -- sum) a + b
sub (a b -- diff) a - b
mul (a b -- product) a * b
div (a b -- quotient) a / b
and (a b -- int) a & b
or (a b -- int) a | b
xor (a b -- int) a ^ b
abs (n -- abs) Absolute value
negate (n -- n)  
complement (val -- val)  
maximum (a b -- max)  
minimum (a b -- min)  
to-rad (deg -- rad) Convert degrees to radians.
to-deg (rad -- deg) Convert radians to degrees.
sin (n -- r) Sine
cos (n -- r) Cosine
tan (n -- r) Tangent
arcsin (n -- r) Arcsine
arccos (n -- r) Arccosine
arctan (n -- r) Arctangent
sqrt (n -- r) Square root
random (max -- n) Generate random number.
random (n 'seed -- ) Seed random number generator.
zero? (n -- logic) True if number is zero.

5.5.1   Vector Math

These functions operate on vec3!/coord!.

Word Stack Usage Function
dot (v1 v2 -- dot) Vector dot product.
cross (v1 v2 -- cross) Vector cross product.
normalize (vec -- vec) Make vec3 unit length.

5.5.2   Math Helpers

Word Stack Usage Function
limit (n min max -- n) Limit number to a range.

5.6   Input/Output

Word Stack Usage Function
read (file -- data) Read file.
read (file part -- data) Read beginning of file.
write (port data -- port) Write data to port.
load (file -- code) Read script from file.
delete (file -- ) Remove file.
rename (file new -- ) Rename file.
print (val -- ) Print reduced value to console.
prin (val -- ) Print reduced value to console (no eol).
prin.pack (val -- ) Print reduced value without spaces.
console.out (str -- ) Print to console (stdout).
open (url [opt] -- port) Open port.
close (port -- ) Close port.
seek (port pos -- port) Set port stream position.
getenv (name -- var) Get environment variable.
full-path (path -- full) Makes a relative directory path absolute.
system.run (cmd -- status) Execute program.
to-hex (int -- int) Flag integer to display in hexadecimal.
to-dec (int -- int) Flag integer to display in decimal.
checksum (bin type -- digest) Compute 'crc16 or 'sha1 checksum.

5.6.1   Input/Output Helpers

Word Stack Usage Function
split-path (full -- path file) Split full path into path & file.
dirize (str -- str) Ensure trailing slash is present.

5.7   Pre-defined Helpers

Word Definition
yes, on true
no, off false
eol '^/'
pi 3.14159265358979323846

7   Flow Control Functions

7.1   ift

(logic -- )

Evaluate next value if TOS is true. To evaluate more than one value use a paren!.

Example:

)> 4 1 gt? ift ("Four is greater than one." print)
Four is greater than one.

7.2   loop

(block int -- )
(block int int -- )

The first form simply evalutes the body block a given number of times.

The second form pushes the counter value onto the stack before each loop, and stops when the limit is reached.

Example:

)> [.] -2 2 loop
-2
-1
0
1

9   Series Functions

9.1   slice

The slice word is used to control the slice range of a series value.

"Hello world" :s
s 2,4 slice .          ; "ello"
s -2 slice .           ; "Hello wor"

To move the start of a series by one, use prev & next. To move the end of a series by one, use slice.prev & slice.next.

9.2   change

(ser new [part int!] -- ser)

Change series at current position. Returns ser at end of change.

9.3   match

(ser pat -- end series!/none!)

Move position to end of matching pattern. Returns none if start of ser does not match pat.

12   Extending The System

12.1   Adding C Functions

C functions are defined with the UR_CALL macro.

The function is responsible for checking that arguments are of the correct type and for reporting any errors. If type checking were built into the evaluator there would be redundant checks for the typical case where the function accepts multiple types for a given argument or pattern of arguments.

Always access and check stack arguments starting with tos and work down. Since the bottom of the stack contains UT_UNSET, checking in this order avoids an extra check to make sure that the stack holds at least the number of expected arguments.