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. |
| (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.
