Files
retrobsd/src/cmd/retroforth/doc/An_Introduction_to_Retro.rst
2014-04-09 14:27:18 +01:00

1200 lines
34 KiB
ReStructuredText

========================
An Introduction to Retro
========================
---------------
Getting Started
---------------
Choosing a VM
=============
Retro runs on a virtual machine. This has been implemented in many languages, and
allows easy portability to most platforms.
The table below lists the current implementations, and the features they support.
For most users, we recommend using *C*, *Python*, or *Ruby*, as these are feature
complete, and can be setup quickly and easily.
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Language | File | A | B | C | D | E | F | G | Building |
+============+==============+===+===+===+===+===+===+===+======================+
| Assembly | retro.s | x | x | x | x | x | x | x | | as retro.s -o retro|
| | | | | | | | | | | ld retro.o -o retro|
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| C | retro.c | x | x | x | x | x | x | x | gcc retro.c -o retro |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| C# | retro.cs | x | x | | x | x | x | x | gmcs retro.cs |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| F# | retro.fsx | x | x | x | x | x | x | x | |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Forth | retro.fs | x | x | x | | x | x | x | |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Go | gonga/ | x | x | x | x | x | x | x | cd gonga && make |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Lisp | retro.lisp | x | | | | x | x | x | |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Java | retro.java | x | | | | x | x | x | javac retro.java |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Lua | retro.lua | x | | | | x | x | x | |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Perl | retro.pl | x | | | | x | x | x | |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Python | retro.py | x | x | x | x | x | x | x | |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
| Ruby | retro.rb | x | x | x | x | x | x | x | |
+------------+--------------+---+---+---+---+---+---+---+----------------------+
A) save image
B) "include", "needs"
C) file i/o
D) query host environment
E) get current time
F) passes core tests
G) passes vocabulary tests
The Image File
==============
Once you have selected (and built, if necessary) a VM, you will need to put it
and the *retroImage* into a directory. You should then be able to start the VM
and interact with Retro.
The Library
===========
If you are using the Assembly, C, F#, Forth, Go, Python, or Ruby VM
implementations, you can also copy or symlink the *library* directory into the
same directory as your VM and *retroImage*.
This is optional, but copying it over is recommended as it simplifies loading
libraries and handling dependencies.
Basic Interactions
==================
When you start Retro, you should see something like the following:
::
Retro 11.1 (1234567890)
ok
At this point you are at the *listener*, which reads and processes your
input. You are now set to begin exploring Retro.
Normally Retro will process input as soon as whitespace is encountered [1]_.
This limits editing options [2]_, but serves to simplify the listener
significantly.
To exit, run **bye**:
::
bye
----------------------
Exploring the Language
----------------------
Names And Numbers
=================
At a fundamental level, the Retro language consists of whitespace delimited
tokens representing either names or numbers.
The *listener* will attempt to look up tokens in the *dictionary*. If found,
the information in the *dictionary header* is used to carry out the actions
specified in the name's *definition*.
If a token can't be found in the dictionary, Retro tries to convert it to
a number. If successful, the number is pushed to the *data stack.* If not,
an error is reported.
Retro permits names to contain any characters other than space, tab, cr, and
lf. Names are *case sensitive*, so the following are three *different* names
from Retro's perspective:
::
foo Foo FOO
The Compiler
============
To create new functions, you use the compiler. This is generally started by using
**:** (pronounced *colon*). A simple example:
::
: foo 1 2 + putn ;
Breaking this apart:
::
: foo
This creates a new function named *foo* and starts the compiler. A **:** should
always be followed by the name of the function to create.
::
1
Normally this would push a *1* to the data stack. However, since the compiler
is active, the listener will compile the code needed to push a *1* to the stack
into the definition instead.
::
2
And again, but compile a *2* instead of a *1*.
::
+
Since *+* is a normal function, the listener compiles a call to it rather than
calling it immediately.
::
putn
**putn** is a function that takes a number from the stack and displays it. When
encountered in a defintion, the compiler will lay down a call to it and continue.
::
;
Functions are terminated with a **;**. This is a special case as **;** is a *compiler
macro*, and is *called at compile time*, but *ignored when the compiler is not
active.*
Hyperstatic Global Environment
==============================
This now brings up an interesting subpoint. Retro provides a *hyper-static
global environment.* This can be difficult to explain, so let's take a quick
look at how it works:
::
: scale ( x-y ) a @ * ;
a ?
1000 variable: a
: scale ( x-y ) a @ * ;
3 scale putn
>>> 3000
100 a !
3 scale putn
>>> 300
5 variable: a
3 scale putn
>>> 300
a @ putn
>>> 5
Output is marked with **>>>**.
Note that we create two variables with the same name (*a*). The definition for
*scale* still refers to the old variable, even though we can no longer directly
manipulate it.
In a hyper-static global environment, functions continue to refer to the variables
and earlier functions that existed when they were defined. If you create a new
variable or function with the same name as an existing one, it only affects future
code.
Classes
=======
Getting back to function creation, it's time for a clarification: in Retro, the
listener is unaware of how to handle a dictionary entry and has no concept of the
difference between compiling and interpreting.
The actual work is handled by something we call *class handlers*.
Each dictionary header contains a variety of information:
+--------+------------------+
| Offset | Description |
+========+==================+
| 0 | link to previous |
+--------+------------------+
| 1 | class handler |
+--------+------------------+
| 2 | xt |
+--------+------------------+
| 3+ | name of function |
+--------+------------------+
When a token is found, the listener pushes the contents of the xt field and the
class handler field to the stack, then calls the **withClass** function. This then
calls the *class handler* function, which does something with the *xt* (pointer
to the actual compiled code or data).
So, when you enter:
::
1 2 +
What actually happens is this:
1. The listener tries to find *1* in the dictionary. This fails, so *1* is pushed
to the stack, and the *.data* class handler is pushed to the stack. *withClass*
then passes control to *.data*.
2. The *.data* class looks at the *compiler* variable, sees that it's off, and then
leaves the *1* on the stack.
3. This is repeated for the *2*.
4. When **+** is encountered, it is found to exist in the dictionary. The *xt* is
pushed to the stack, and the *.word* class handler is pushed. Then *withClass*
is called.
5. *withClass* passes control to *.word*, which checks *compiler*, sees that it is
off, and then calls the *xt* corresponding to the definition of **+**.
When you create a definition, the flow is altered slightly:
1. The listener tries to find *1* in the dictionary. This fails, so *1* is pushed
to the stack, and the *.data* class handler is pushed to the stack. *withClass*
then passes control to *.data*.
2. The *.data* class looks at the *compiler* variable, sees that it's on, and lays
down the code needed to push *1* to the stack.
3. This is repeated for the *2*.
4. When *+* is encountered, it is found to exist in the dictionary. The *xt* is
pushed to the stack, and the *.word* class handler is pushed. Then *withClass*
is called.
5. *withClass* passes control to *.word*, which checks *compiler*, sees that it is
on, so compiles the necessary code to call the *xt* corresponding to the
definition of *+*.
This model differs from Forth (and most other languages) in that the listener is
kept out of the loop. All actions are handled by the function classes. A useful
side effect is that additional classes can be created at any time, and assigned
to any named functions or data structures.
The following classes are defined by default:
+------------+-----------------------------------------------------------+
| Function | Description |
+============+===========================================================+
| .word | This is the class handler for normal functions. If the |
| | *compiler* is off, it executes the function passed to it. |
| | If the *compiler* is on, it compiles a call to the |
| | function. |
+------------+-----------------------------------------------------------+
| .compiler | This class handler is used for functions that act as |
| | compile-time macros. The function pointer is executed if |
| | the *compiler* is on. If off, it ignores pointer. |
+------------+-----------------------------------------------------------+
| .primitive | Used for a small set of functions that can map directly to|
| | Ngaro instructions. This acts the same as *.word*, but |
| | inlines the machine code at compile time rather than lay |
| | down a call. |
+------------+-----------------------------------------------------------+
| .macro | Used for general macros. Functions with this class are |
| | always executed. |
+------------+-----------------------------------------------------------+
| .data | This is used for data structures. If *compiler* is off, it|
| | leaves the pointer on the stack. If the *compiler* is on |
| | this compiles the value into another function. |
+------------+-----------------------------------------------------------+
| .parse | Special class used for *parsing prefixes*. Acts the same |
| | as *.macro* |
+------------+-----------------------------------------------------------+
By default, colon definitions are given a class of *.word*, and entries made
by **create**, **variable**, and **constant** get a class of *.data*. To assign
the *.macro* class or the *.compiler* class, use either **immediate** or
**compile-only** after the **;**.
Data Structures
===============
You can create named data structures using **create**, **variable**,
**variable:**, **constant**, and **elements**.
Constants
---------
These are the simplest data structure. The *xt* is set to a value, which is
either left on the stack or compiled into a definition.
::
100 constant ONE-HUNDRED
By convention, constants in Retro should have names in all uppercase.
Variables
---------
A variable is a named pointer to a memory location holding a value that may change
over time. Retro provides two ways to create a variable:
::
variable a
The first, using **variable**, creates a name and allocates one cell for storage.
The memory is initialized to zero.
::
10 variable: b
The second, **variable:**, takes a value from the stack, and creates a name,
allocates one cell for storage, and then initializes it to the value specified.
This is cleaner than doing:
::
variable a
10 a !
Custom Structures
-----------------
You can also create custom data structures by creating a name, and allocating
space yourself. For instance:
::
create test
10 , 20 , 30 ,
This would create a data structure named *test*, with three values, initialized
to 10, 20, and 30. The values would be stored in consecutive memory locations.
If you want to allocate a buffer, you could use **allot** here:
::
create buffer
2048 allot
The use of **allot** reserves space, and initializes the space to zero.
Elements
--------
Elements are a hybrid between variables and custom data structures. They create
a series of names that point to consecutive cells in memory.
::
3 elements a b c
100 a !
200 b !
300 c !
a @+ putn
>>> 100
@+ putn
>>> 200
@ putn
>>> 300
Strings
-------
In addition to the basic data structures above, Retro also provides support for
string data.
Creating a string simply requires wrapping text with quotation marks:
::
"this is a string"
" this string has leading and trailing spaces "
When creating strings, Retro uses a floating, rotating buffer for temporary
strings. Strings created in a definition are considered permanent.
You can obtain the length of a string using either **getLength** or **withLength**:
::
"this is a string" getLength
"this is also a string" withLength
**getLength** will consume the string pointer, while **withLength** preserves it.
Comparisons
-----------
Strings can be compared using **compare**:
::
"test 1" "test 2" compare putn
>>> 0
"test" "test" compare putn
>>> -1
The comparisons are case sensitive.
Searching
---------
For a Substring
```````````````
Substrings can be located using **^strings'search**. This will return a pointer
to the location of the substring or a flag of 0 if the substring is not found.
::
"this is a long string"
"a long" ^strings'search
.s puts
For a Character
```````````````
Searching for specific characters in a string is done using **^strings'findChar**.
This will return a pointer to the string starting with the character, or a flag
if 0 if the character is not found.
::
"this is a string"
'a ^strings'findChar
.s puts
Extracting a Substring
----------------------
Retro provides three functions for splitting strings.
The first, **^strings'getSubset**, takes a string, a starting offset, and a
length. It then returns a new string based on the provided values.
::
"this is a string"
5 8 ^strings'getSubset
.s puts
The other two are **^strings'splitAtChar** and **^strings'splitAtChar:**. The
first form takes a string and character from the stack and returns two
strings. The second takes a string and parses for a character.
::
"This is a test. So is this" '. ^strings'splitAtChar puts puts
"This is a test. So is this" ^strings'splitAtChar: . puts puts
Trim Whitespace
---------------
Leading whitespace can be removed with **^strings'trimLeft** and trailing
whitespace with **^strings'trimRight**.
::
: foo
cr " apples" ^strings'trimLeft puts
"are good! " ^strings'trimRight puts
100 putn ;
foo
Append and Prepend
------------------
To append strings, use **^string'append**. This consumes two strings, returning
a new string starting with the first and ending with the second.
::
"hello," " world!" ^strings'append puts
A varient exists for placing the second string first. This is
**^strings'prepend**.
::
: sayHelloTo ( $- ) "hello, " ^strings'prepend puts cr ;
"world" sayHelloTo
Case Conversion
---------------
To convert a string to uppercase, use **^strings'toUpper**.
::
"hello" ^strings'toUpper puts
To convert a string to lowercase, use **^strings'toLower**.
::
"Hello Again" ^strings'toLower puts
Reversal
--------
To reverse the order of the text in a string, use **^strings'reverse**.
::
"hello, world!" ^strings'reverse puts
Implementation Notes
--------------------
Strings in Retro are null-terminated. They are stored in the image memory. E.g.,
assuming a starting address of 12345 and a string of "hello", it will look like
this in memory:
::
12345 h
12346 e
12347 l
12348 l
12349 o
12350 0
You can pass pointers to a string on the stack.
Prefixes
========
Before going further, let's consider the use of prefixes in Retro. The earlier
examples involving variables used **@** and **!** (for *fetch* and *store*) to access
and modify values. Retro allows these actions to be bound to a name more tightly:
::
variable a
variable b
100 !a
@a !b
This would be functionally the same as:
::
variable a
variable b
100 a !
a @ b !
You can mix these models freely, or just use what you prefer. I personally find
that the prefixes make things slightly clearer, but most of them are completely
optional [3]_.
Other prefixes include:
+----------+--------------------------------------------------+
| Function | Description |
+==========+==================================================+
| & | Return a pointer to a function or data structure |
+----------+--------------------------------------------------+
| ``+`` | Add TOS to the value stored in a variable |
+----------+--------------------------------------------------+
| ``-`` | Subtract TOS from the value stored in a variable |
+----------+--------------------------------------------------+
| @ | Return the value stored in a variable |
+----------+--------------------------------------------------+
| ! | Store TOS into a variable |
+----------+--------------------------------------------------+
| ^ | Access a function or data element in a vocabulary|
+----------+--------------------------------------------------+
| ' | Return ASCII code for following character |
+----------+--------------------------------------------------+
| $ | Parse number as hexadecimal |
+----------+--------------------------------------------------+
| # | Parse number as decimal |
+----------+--------------------------------------------------+
| % | Parse number as binary |
+----------+--------------------------------------------------+
| " | Parse and return a string |
+----------+--------------------------------------------------+
Quotes
======
In addition to colon definitions, Retro also provides support for anonymous,
nestable blocks of code called *quotes*. These can be created inside definitions,
or at the interpreter.
Quotes are essential in Retro as they form the basis for conditional execution,
loops, and other forms of flow control.
To create a quote, simply wrap a sequence of code in square brackets:
::
[ 1 2 + putn ]
To make use of quotes, Retro provides *combinators*.
Combinators
===========
A combinator is a function that consumes functions as input. These are
divided into three primary types: compositional, execution flow, and data
flow [4]_.
Compositional
-------------
A compositional combinator takes elements from the stack and returns a
new quote.
**cons** takes two values from the stack and returns a new quote that
will push these values to the stack when executed.
::
1 2 cons
Functionally, this is the same as:
::
[ 1 2 ]
**take** pulls a value and a quote from the stack and returns a new
quote executing the specified quote before pushing the value to the
stack.
::
4 [ 1+ ] take
Functionally this is the same as:
::
[ 1+ 4 ]
**curry** takes a value and a quote and returns a new quote applying
the specified quote to the specified value. As an example,
::
: acc ( n- ) here swap , [ dup ++ @ ] curry ;
This would create an accumulator function, which takes an initial value
and returns a quote that will increase the accumulator by 1 each time it
is invoked. It will also return the latest value. So:
::
10 acc
dup do putn
dup do putn
dup do putn
Execution Flow
--------------
Combinators of this type execute other functions.
Fundamental
```````````
**do** takes a quote and executes it immediately.
::
[ 1 putn ] do
&words do
Conditionals
````````````
Retro provides four combinators for use with conditional execution of
quotes. These are **if**, **ifTrue**, **ifFalse**, and **when**.
**if** takes a flag and two quotes from the stack. If the flag is
*true*, the first quote is executed. If false, the second quote is
executed.
::
-1 [ "true\n" puts ] [ "false\n" puts ] if
0 [ "true\n" puts ] [ "false\n" puts ] if
**ifTrue** takes a flag and one quote from the stack. If the flag is true,
the quote is executed. If false, the quote is discarded.
::
-1 [ "true\n" puts ] ifTrue
0 [ "true\n" puts ] ifTrue
**ifFalse** takes a flag and one quote from the stack. If the flag is false,
the quote is executed. If true, the quote is discarded.
::
-1 [ "false\n" puts ] ifFalse
0 [ "false\n" puts ] ifFalse
**when** takes a number and two quotes. The number is duplicated, and the
first quote is executed. If it returns true, the second quote is executed.
If false, the second quote is discarded.
Additionally, if the first quote is true, **when** will exit the calling
function, but if false, it returns to the calling function.
::
: test ( n- )
[ 1 = ] [ drop "Yes\n" puts ] when
[ 2 = ] [ drop "No\n" puts ] when
drop "No idea\n" puts ;
Looping
```````
Several combinators are available for handling various looping constructs.
**while** takes a quote from the stack and executes it repeatedly as long
as the quote returns a *true* flag on the stack. This flag must be well
formed and equal *-1*.
::
10 [ dup putn space 1- dup 0 <> ] while
**times** takes a count and quote from the stack. The quote will be executed
the number of times specified. No indexes are pushed to the stack.
::
1 10 [ dup putn space 1+ ] times
The **iter** and **iterd** varients act similarly, but do push indexes to
the stacks. **iter** counts up from 0, and **iterd** counts downward to 1.
::
10 [ putn space ] iter
10 [ putn space ] iterd
Data Flow
`````````
These combinators exist to simplify stack usage in various circumstances.
Preserving
``````````
Preserving combinators execute code while preserving portions of the data stack.
**dip** takes a value and a quote, moves the value off the main stack
temporarily, executes the quote, and then restores the value.
::
10 20 [ 1+ ] dip
Would yield the following on the stack:
::
11 20
This is functionally the same as doing:
::
10 20 push 1+ pop
**sip** is similar to **dip**, but leaves a copy of the original value on
the stack during execution of the quote. So:
::
10 [ 1+ ] sip
Leaves us with:
::
11 10
This is functionally the same as:
::
10 dup 1+ swap
Cleave
``````
Cleave combinators apply multiple quotations to a single value or set
of values.
**bi** takes a value and two quotes, it then applies each quote to a
copy of the value.
::
100 [ 1+ ] [ 1- ] bi
**tri** takes a value and three quotes. It then applies each quote to a
copy of the value.
::
100 [ 1+ ] [ 1- ] [ dup * ] tri
Spread
``````
Spread combinators apply multiple quotations to multiple values. The asterisk
suffixed to these function names signifies that they are spread combinators.
**bi*** takes two values and two quotes. It applies the first quote to the
first value and the second quote to the second value.
::
1 2 [ 1+ ] [ 2 * ] bi*
**tri*** takes three values and three quotes, applying the first quote to
the first value, the second quote to the second value, and the third quote
to the third value.
::
1 2 3 [ 1+ ] [ 2 * ] [ 1- ] tri*
Apply
`````
Apply combinators apply a single quotation to multiple values. The at sign
suffixed to these function names signifies that they are apply combinators.
**bi@** takes two values and a quote. It then applies the quote to each value.
::
1 2 [ 1+ ] bi@
**tri@** takes three values and a quote. It then applies the quote to each
value.
::
1 2 3 [ 1+ ] tri@
**each@** takes a pointer, a quote, and a type constant. It then applies the
quote to each value in the pointer. In the case of a linear buffer, it also
takes a length.
::
( arrays )
create a 3 , ( 3 items ) 1 , 2 , 3 ,
a [ @ putn space ] ^types'ARRAY each@
( buffer )
"hello" withLength [ @ putc ] ^types'BUFFER each@
( string )
"HELLO" [ @ putc ] ^types'STRING each@
( linked list )
last [ @ d->name puts space ] ^types'LIST each@
Conditionals
============
Retro has a number of functions for implementing comparisons and conditional
execution of code.
Comparisons
-----------
+----------+-----------+-----------------------------------------+
| Function | Stack | Description |
+==========+===========+=========================================+
| = | ab-f | compare a == b |
+----------+-----------+-----------------------------------------+
| > | ab-f | compare a > b |
+----------+-----------+-----------------------------------------+
| < | ab-f | compare a < b |
+----------+-----------+-----------------------------------------+
| >= | ab-f | compare a >= b |
+----------+-----------+-----------------------------------------+
| <= | ab-f | compare a <= b |
+----------+-----------+-----------------------------------------+
| <> | ab-f | compare a <> b |
+----------+-----------+-----------------------------------------+
| compare | $$-f | compare two strings |
+----------+-----------+-----------------------------------------+
| if; | f- | if flag is true, exit function |
+----------+-----------+-----------------------------------------+
| 0; | n-? | if n <> 0, leave n on stack and continue|
| | | if n = 0, drop n and exit function |
+----------+-----------+-----------------------------------------+
| if | fqq- | Execute one of two quotes depending on |
| | | value of flag |
+----------+-----------+-----------------------------------------+
| ifTrue | fq- | Execute quote if flag is not zero |
+----------+-----------+-----------------------------------------+
| ifFalse | fq- | Execute quote if flag is zero |
+----------+-----------+-----------------------------------------+
| when | nqq-n | Execute second quote if first quote |
| | | returns true. Exits caller if second |
| | | quote is executed. |
+----------+-----------+-----------------------------------------+
Namespaces
==========
Sometimes you will want to hide some functions or data structures from the
main dictionary. This is done by wrapping the code in question in double
curly braces:
::
23 constant foo
{{
1 constant ONE
2 constant TWO
: foo ONE TWO + ;
foo
}}
foo ( refers to the first foo; the second foo is now hidden )
When the closing braces are encountered, the headers for the functions following
the opening braces are hidden.
If you want to hide some functions, but reveal others, you can add **---reveal---**
into the mix:
::
{{
1 constant ONE
2 constant TWO
---reveal---
: foo ONE TWO + ;
}}
At this point, *foo* would be visible, but the constants would be hidden.
Vocabularies
============
Vocabularies allow grouping of related functions and data, and selectively
exposing them. Active vocabularies are searched before the main dictionary
and the order for searching is configurable at runtime.
Creation
--------
::
chain: name'
...functions...
;chain
Vocabulary names should generally be lowercase, and should end with a single
apostrophe.
Exposing and Hiding
-------------------
Use **with** to add a vocabulary to the search order. The most recently
exposed vocabularies are searched first, with the global dictionary
searched last.
::
with console'
The most recent vocabulary can be closed using **without**.
::
without
You can also close all vocabularies using **global**.
::
global
As a simplification, you can reset the search order and load a series
of vocabularies using **with|**:
::
with| console' files' strings' |
Direct Access
-------------
It is possible to directly use functions and variables in a vocabulary
using the **^** prefix.
::
^vocabulary'function
As an example:
::
: redWords ^console'red words ^console'normal ;
This is recommended over exposing a full vocabulary as it keeps the
exposed functions down, helping to avoid naming conflicts.
Vectored Execution
==================
One of the design goals of Retro is flexibility. And one way this is achieved is
by allowing existing colon definitions to be replaced with new code. We call this
*revectoring* a definition.
+-----------+-------+----------------------------------------------------+
| Function | Stack | Description |
+===========+=======+====================================================+
| :is | aa- | Assign the function (a2) to act as (a1) |
+-----------+-------+----------------------------------------------------+
| :devector | a- | Restore the original definition of (a) |
+-----------+-------+----------------------------------------------------+
| is | a"- | Parse for a function name and set it to act as (a) |
+-----------+-------+----------------------------------------------------+
| devector | "- | Parse for a function name and restore the original |
| | | definition |
+-----------+-------+----------------------------------------------------+
Example:
::
: foo ( -n ) 100 ;
: bar ( - ) foo 10 + putn ;
bar
>>> 110
[ 20 ] is foo
bar
>>> 30
devector foo
bar
>>> 110
This technique is used to allow for fixing of buggy code in existing images
and adding new functionality.
Input and Output
================
Getting away from the quotes, combinators, compiler, and other bits, let's take
a short look at input and output options.
Console
-------
At the listener level, Retro provides a few basic functions for reading and
displaying data.
+----------+-------+--------------------------------------------------------+
| Function | Stack | Description |
+==========+=======+========================================================+
| getc | -c | Read a single keypress |
+----------+-------+--------------------------------------------------------+
| accept | c- | Read a string into the text input buffer |
+----------+-------+--------------------------------------------------------+
| getToken | -$ | Read a whitespace delimited token and return a pointer |
+----------+-------+--------------------------------------------------------+
| putc | c- | Display a single character |
+----------+-------+--------------------------------------------------------+
| puts | $- | Display a string |
+----------+-------+--------------------------------------------------------+
| clear | ``-`` | Clear the display |
+----------+-------+--------------------------------------------------------+
| space | ``-`` | Display a blank space |
+----------+-------+--------------------------------------------------------+
| cr | ``-`` | Move cursor to the next line |
+----------+-------+--------------------------------------------------------+
The **puts** function handles a number of escape sequences to allow for formatted
output.
+------+------------------------------------------------+
| Code | Use |
+======+================================================+
| \n | newline |
+------+------------------------------------------------+
| \[ | ASCII 27, followed by [ |
+------+------------------------------------------------+
| \\ | Display a \ |
+------+------------------------------------------------+
| \' | Display a " |
+------+------------------------------------------------+
| %d | Display a number from the stack (decimal) |
+------+------------------------------------------------+
| %o | Display a number from the stack (octal) |
+------+------------------------------------------------+
| %x | Display a number from the stack (hexadecimal) |
+------+------------------------------------------------+
| %s | Display a string from the stack |
+------+------------------------------------------------+
| %c | Display a character from the stack |
+------+------------------------------------------------+
| %% | Display a % |
+------+------------------------------------------------+
As an example:
::
3 1 2 "%d + %d = %d\n" puts
>>> 2 + 1 = 3
: I'm ( "- )
getToken "\nHello %s, welcome back.\n" puts ;
I'm crc
>>> Hello crc, welcome back
---------
Footnotes
---------
.. [1] With some VM implementations, Retro will not process the input until
the enter key is pressed. This is system-level buffering, and is not
the standard Retro behavior. There are external tools included with
Retro to alter the behavior to match the standard.
.. [2] You can not use Retro with tools like *rlwrap*, and editing is limited
to use of backspace. The arrow keys are not supported by Retro.
.. [3] The exceptions here would be the *&* prefix for obtaining a pointer inside
a definition and the *"* prefix for parsing strings. All of the others can
be worked around or ignored easily.
.. [4] The terminology and some function names are borrowed from the Factor
language.