Rebol Coreguide
Rebol Coreguide
Version 2.3
Send comments to docs@rebol.com
# Chapter Description
Introduction
Updated: 7-Jul-2001
Table of Contents
1. About REBOL/Core
2. About this Guide
3. Document Conventions
4. Technical Support
5. Comments are Welcome
1. About REBOL/Core
REBOL is the Relative Expression-Based Object Language designed by Carl Sassenrath,
the software architect responsible for the Amiga operating system -- one of the world's first
personal computer multitasking operating systems.
REBOL is designed for the next generation of distributed communications. REBOL code and
data can span more than 40 platforms without modification using a range of built-in Internet
protocols. A script written and executed on the Windows platform can also be run on UNIX
and many other platforms with no changes. REBOL can exchange not only traditional files
and text, but also graphical user interface content and domain specific dialects that
communicate specific meaning between systems. Distributed communications includes
information exchanged between computers, between people and computers, and between
people. REBOL can be used for all of these.
REBOL is a messaging language that provides a broad range of practical solutions to the
daily challenges of Internet computing. REBOL/Core is the foundation for all of REBOL's
technology. While designed to be simple and productive for novices, the language extends a
new dimension of power to professionals. REBOL offers a new approach to the exchange
and interpretation of network-based information over a wide variety of computing platforms.
REBOL scripts are as easy to write as HTML or shell scripts. A script can be a single line or
an entire application.
3. Document Conventions
The following table describes the typographical conventions used in this guide.
Non-REBOL words
and values such as
file names, directory
names, program Italic style type myfile window-color
names, and variable
names.
The best way to contact us is to run the REBOL script, feedback.r, which is included as part
of the REBOL disribution directory. It can be run by typing the line below at the console
prompt:
do %feedback.r
This script presents a menu that guides you though the feedback process. Using the
feedback script automatically includes your REBOL version number in the email sent to
REBOL's help desk.
Operation
Updated: 8-Jul-2001
Table of Contents
1. Installing REBOL
1.1. Distribution Files
1.2. Network Setup
1.3. Proxy and Firewall Settings
1.4. License Agreement
2. Starting REBOL
2.1. From an Icon
2.2. From a Shell
2.3. From Another Application
2.4. Security Issues
2.4.1. Port Security
2.4.2. Prior Security Settings
2.5. Program Arguments
2.6. Script File
2.7. Specifying Options
2.8. File Redirection
2.9. Script Arguments
2.10. Startup Files
3. Quitting REBOL
4. Using the Console
4.1. Mulitple Line Input
4.2. Interrupting a Script
4.3. History Recall
4.4. Word Completion
4.5. Busy Indicator
4.6. Network Connections
4.7. Virtual Terminal
5. Getting Help
5.1. Online Help
5.2. Viewing Source Code
5.3. Download Documents
5.4. Script Library
5.5. User Mailing List
5.6. Contacting Us
6. Errors
6.1. Error Messages
6.2. Redirecting Errors
7. Upgrading
1. Installing REBOL
REBOL installation takes only a few seconds and is very easy, non-intrusive, and non-disruptive.
For REBOL/Core the only installation procedure is to uncompress the distribution files and store them in any directory
on your system. In addition, some operating systems such as UNIX, require an environment variable, REBOL_HOME,
to help REBOL find its bootstrap files.
For other REBOL products, installation may require you to provide additional information, such as where to store
related files. Refer to the release notes that are included with the distribution files.
The first time you start REBOL, it prompts you for network information. This information is optional. Some protocols,
such as email or FTP, require an email address or an email server name. In addition, if you are behind a firewall or use
a proxy server, you need to provide specific information to access the Internet.
Once the startup questions are answered, REBOL creates a user.r file and places your network settings in it. You can
change these settings at any time by editing the user.r file.
1.3. Proxy and Firewall Settings
Frequently, organizations use a firewall or proxy server to protect access to and from the Internet. Before REBOL can
access the Internet through these systems, you need to provide some additional information.
1. When REBOL asks if you use a proxy server, answer by typing Y (yes).
2. Type the name of your proxy host. This is the computer or firewall on your network serving as a proxy.
3. Type the port number used by the proxy host for proxy requests. Typically, this is port 1080, but this can vary.
If you don't know the port number, check your Web browser settings or ask your network administrator.
REBOL defaults to using a SOCKS proxy protocol. You can specify some other type of proxy by editing the user.r file
and supplying the set-net function with the appropriate identification for the type of proxy being used. The following
settings are supported:
These settings are provided as the sixth argument to the set-net function called in the user.r file. For more information
about modifying the proxy settings in the user.r file, refer to the Network Protocols Chapter.
The REBOL end-user license agreement that you agreed to when you downloaded or installed REBOL can be viewed
at any time from the REBOL console by typing license at the REBOL prompt.
2. Starting REBOL
REBOL runs on a large variety of systems. You start REBOL the same way you start other applications on your
system. Depending on the specific operating system, REBOL can be started from one or more of the following: an
icon, the command shell, or other applications.
REBOL can be started by double-clicking the REBOL program icon, an associated .r file, or a REBOL shortcut icon.
If you double-click on the program icon, REBOL boots, displays the console, and provides you with a prompt.
If you want to launch REBOL with a script, you can do so in the following ways:
From a shell command line, go to the directory that contains the rebol.exe file (or rebol on non-Windows systems), and
type rebol or ./rebol.
On some operating systems, such as UNIX, you can create alias shell commands that are able to run REBOL with a
set of arguments and files. In addition, UNIX enables you to create shell scripts that include a path, such as:
!#/path/to/rebol
in the top line of the script file. When you type the name of the script file at the command prompt, UNIX will launch
REBOL to execute the script.
For writing and debugging REBOL scripts, it is handy to set up your favorite text editor to run REBOL and pass it the
script file you are editing. Each text editor does this differently.
For instance, in the Premia Codewright editor you can use the language compiler options to set up REBOL. Specify
the REBOL program rather than a compiler. Then you can press a single key that saves the script and evaluates it.
By default, security is set to prevent scripts from modifying any of your files or directories.
The secure function provides flexibility in setting and controlling the security features of REBOL. The current security
settings are returned as a result of calling the secure function.
Security settings use a REBOL dialect, that is, a language within a language. The normal dialect consists of a block of
paired values. The first value in the pair specifies what is being secured:
A file name or directory path allows you to specify security levels for a specific file or directory.
The second value in the pair specifies the level of security. This can be either a security level word or a block of words.
The security level words are:
For example, to allow all network access, but to quit on any file access:
secure [
net allow ;allows any net access
file quit ;any file access will cause the program to quit
]
If a block is used instead of a security level word, it can contain pairs of security levels and access types. This lets you
specify a greater level of detail about the security you require. The access types allowed are:
The pairs are processed in the order they appear, with later pairs modifying the effect of earlier pairs. This permits
setting one type of access without explicitly setting all others. For example:
secure [
net allow
file [
ask all
allow read
]
]
The above sets the security level to ask for all operations except for reading which is to be allowed. This technique
can also be used for individual files and directories. For example:
secure [
net allow
file quit
%source/ [ask read]
]
asks if an attempt is made to read the %source directory. Otherwise, it uses the default (quit).
There is a special case in which the secure function takes a single word argument that must be one of the security
access levels. In that case, the security level for all network and file access is set to that level.
secure quit
The secure function also accepts none, allowing access with no restrictions (same as allow ).
secure none
secure [
net allow
file [
ask all
allow read
]
]
If no security access level is specified for either network or file access, it defaults to ask. The current settings will not
be modified if an error occurs parsing the security block argument.
The secure function now returns the prior security settings before the new settings were made. This is a block with the
global network and file settings followed by file or directory settings. The query word can be used to obtain the settings
without modifying them.
You can modify the current security level by querying the current settings, modifying them, then using the secure
function to set the new values.
Lowering the security level produces a change security settings request. The exception is when the REBOL session is
running in quiet mode which will, instead, terminate the REBOL session. No query is generated when security levels
are raised. Note that the security request now includes an option to allow all access for the remainder of the scripts
processing.
When running REBOL from the shell, the -s argument is equivalent to:
secure allow
secure quit
You can now follow the --secure argument with one of the security access levels for both network and file access:
There are a number of arguments that can be specified in a shell command line, in a batch script, or in the properties
of an icon. To view the arguments and options available for any version of the REBOL language, type usage at the
console prompt.
Examples:
REBOL script.r
REBOL script.r 10:30 test@domain.dom
REBOL script.r --do "verbose: true"
REBOL --cgi -s
REBOL --cgi --secure throw --script cgi.r "debug: true"
REBOL --secure none
Where:
options one or more of the program options. See Specifying Options below for more details.
script the file name of the script you want to run. If the file name contains spaces, it should be
typed in quotes.
arguments the arguments passed to the script as a string. These arguments can be accessed from
within the script.
All of the above arguments are optional, and any combination is permitted.
Shortcut Icons
In some operating systems, like Windows or AmigaOS, you can create icons that supply any
of the above options as part of the icon. Using this technique, you can create icons that
directly execute REBOL scripts with the correct options.
Typically, you run REBOL with the file name of the script that you want it to evaluate. Only one script file is allowed.
For example:
REBOL script.r
Program options are identifed with a plus sign (+) or minus sign (-) before a single character or by a double dash (--)
before a full word. This is a standard practice for specifying program options on most operating systems.
To run a script with an option, such as the -s option, which evaluates the script with security turned off, type:
REBOL -s script.r
REBOL -?
REBOL --help
To run REBOL without opening a new window (this is done when you need to redirect output to a file or server), type:
REBOL -w
REBOL --nowindow
To prevent the printout of startup information which is useful if you are redirecting the output to a file or server, type:
REBOL -q
REBOL --quiet
REBOL -s script.r
REBOL --secure none script.r
To use REBOL scripts for CGI (see the CGI - Common Gateway Interface Section of the Network Protocols Chapter
for more information), type:
REBOL -c cgi-script.r
REBOL --cgi
Multiple options are also allowed. Multiple single character options can be included together. Multiple full word options
must be separated with spaces.
The above example runs in CGI mode, with security turned off. The shorthand method is required for various web
servers that restrict the number of arguments allowed on the command line (such as the Apache server on Linux).
2.8. File Redirection
On most systems, it is possible to redirect standard input and output from and to files. The example:
Use the -w option to prevent the REBOL console window from opening, as it interferes with
standard input and output redirection.
Everything on the command line that follows the script file name is passed to the script as its argument. This allows
you to write scripts that accept arguments directly from the command line.
The script in the above example is passed these arguments in the system object. To print the arguments that have
been passed, type:
probe system/script/args
["10:30" "test@domain.dom"]
When REBOL starts, it attempts to load the rebol.r and user.r boot files. These files are optional, but when found, they
can be used to set up networking, define common functions, and initialize data used by scripts.
The rebol.r script file holds special functions or extensions to REBOL that are provided as part of the standard
distribution. It is suggested that you do not edit this file as it is overwritten with each new release of REBOL.
The user.r script file holds user preferences. You can edit this file and add whatever definitions or data you require.
On multi-user systems, there can be a different user.r for every user. While the user.r file is not part of the distribution,
it is automatically generated if it does not exist.
When REBOL starts, it looks for the rebol.r and user.r files first in the current directory. If the files are not found,
REBOL looks in a directory that is specified with the operating system environment variable REBOL_HOME or by
examining the contents of the .rebol file in your user home directory.
To provide a home directory, you can set an environment variable in the appropriate login or startup script for your
system. For example, on Windows NT you can add:
set REBOL_HOME=C:\REBOL
On Unix systems, you can set the path to REBOL by adding a line like the following in your login shell script or profile:
set REBOL_HOME=/usr/bin/rebol
For some versions of REBOL, the path is stored in a .rebol file that is located in your home directory.
3. Quitting REBOL
To exit REBOL at any time, select Quit from the Console File menu or by typing quit or q at the prompt.
The REBOL console may also quit if an error occurs during startup.
Do not use the word exit to quit REBOL. This word is used for exiting functions and it will
return an error if typed at the console.
>>
If you type an expression at the input prompt, it is evaluated and any returned values are displayed following the result
indicator:
==
For example:
>> 100 + 20
== 120
== 20341
The prompt characters can be changed. See the Console Appendix for more information.
The console also becomes active if a script encounters an error or if the script calls the halt function.
If you begin a block on the command line and don't end it, the block is extended to the next line. This is indicated by a
prompt that begins with a bracket and is followed by indentation. The line will be indented four spaces for each open
block. For example:
loop 10 [
[ print "example"
[ if odd? random 10 [
[ print "here"
[ ]
[ ]
Brackets and braces that appear within quoted strings are ignored. You can escape from input at any time by pressing
the ESCAPE key.
A script can be interrupted by pressing the ESCAPE key, which returns immediately to the command prompt.
During some types of operating system or network activity there may be a delay in responding to the ESCAPE
interrupt.
Each line that is typed into REBOL is stored for later recall. The up and down arrow keys are used to scroll through the
list of previous lines. For instance, pressing the up arrow once recalls the prior input line.
History lines can be written to a file by saving the history block. See the Console Appendix for more information.
4.4. Word Completion
To help speed typing of long words and file names, the REBOL console has word and file name completion. After
typing a few letters of a word, press the tab key. If the letters uniquely identify the word, the rest of the word is
displayed. For example, typing:
>> sq
>> square-root
If the letters do not uniquely identify the word, you can press tab again to get a list of choices. For example, typing:
>> so
so
and you can type the rest of the word or enough of it to be unique.
Completion works for all words, including user-defined words. It also works for files when they are preceded by a
percent sign.
When REBOL waits for a network operation to complete, a busy indicator appears to indicate that something is
happening. You can change the indicator to your own character pattern. See the Console Appendix for more
information.
As network connections are initiated, a message appears on the console. For instance, typing:
The console provides virtual terminal capability that allows you to perform operations such as cursor movement, cursor
addressing, line editing, screen clearing, control key input, and cursor position querying.
The virtual terminal uses the standard ANSI character sequences. This allows you to write platform-independent
terminal programs such as text editors, email clients, or telnet emulators.
5. Getting Help
Several sources of information exist: online help built into REBOL, the source function, documents on the REBOL web
site, the REBOL script library, the REBOL mailing list, and sending feedback to REBOL.
The online help function provides a quick way to obtain summary information about REBOL words. There are several
ways to use help.
help insert
help find
help "path"
help to-
help native!
help datatype!
If you provide a function word as an argument, help prints all of the information that was provided about the function.
For instance, if you type:
help insert
USAGE:
INSERT series value /part range /only /dup count
DESCRIPTION:
Inserts a value into a series and returns the series after the insert.
INSERT is an action value.
ARGUMENTS:
series -- Series at point to insert (Type: series port bitset)
value -- The value to insert (Type: any-type)
REFINEMENTS:
/part -- Limits to a given length or position.
range -- (Type: number series port)
/only -- Inserts a series as a series.
/dup -- Duplicates the insert a specified number of times.
count -- (Type: number)
The help function also finds words that contain a specified string. For instance, to find all of the words that include the
string path, type:
? "path"
You can also search for all globally defined words that are of a given data type. For example, to list all words that are
function! data types, type:
? function!
? datatype!
The help function does not provide useful information about the objects of the system, for
example:
help system/options/home
system/options/home is a path.
help system
as it can take several minutes and produce over a megabyte of text output.
source join
join: func [
"Concatenates values."
value "Base value"
rest "Value or block of values"
][
value: either series? value [copy value] [form value]
repend value rest
]
REBOL defined functions include the mezzanine functions (built-in functions implemented in REBOL) and user
defined functions. Native functions are built-in functions implemented in machine code, and their source cannot be
displayed.
Check the REBOL Web site http://www.rebol.com/ for a list of the current documentation.
In addition to this manual, there is a REBOL Dictionary that covers all the predefined words available in REBOL. If the
console help or this guide does not contain sufficient information about a REBOL word, look in the dictionary for a
detailed description.
The dictionary is updated with each release of REBOL and is available at http://www.REBOL.com/docs/dictionary.html.
The REBOL Web site contains a library with numerous useful debugged scripts that cover a variety of topics. The
library is divided into categories to make it easy to find a script specific to a given function. You can also search the
library for scripts that contain a specific word.
You can also obtain help from a community of REBOL users by joining the REBOL email discussion list. To sign up,
send an email to rebol-request@rebol.com with the subject line containing the word "subscribe". For example:
Be sure that your correct email address has been set up in advance with set-net.
5.6. Contacting Us
We want to know what you think; please contact us to:
You can contact the REBOL Technologies support group by sending an email message to feedback@rebol.com.
Another way to provide feedback is to run the feedback.r script that is part of the distribution. Type:
do %feedback.r
This script presents a menu to help guide you though the feedback process.
Using the feedback script automatically includes the version number of REBOL release you are using in the email sent
to REBOL's helpdesk. If you contact us directly at feedback, please provide the version number of the product you are
using.
6. Errors
There are several types of errors within REBOL. When an error occurs a message is displayed that tells you what the
error was and approximately where it occurred. For instance if you type:
abc
The type of error is indicated by the first few words of the message. In the above example, the error is a Script Error.
Script errors are the most common and occur when you use a function of the language in the wrong way or with
improper arguments. Other types of errors are described in Error Types.
Syntax errors Occur when the script contains an invalid value or a missing header, quote, bracket, or parenthesis.
Math errors Occur when dividing a number by zero or there was a math overflow or underflow.
Occur when a file, directory, or network operation cannot be accessed or access permissions are
Access errors
restricted.
Throw errors Occur when a break, exit, or throw is used in an improper manner.
Returned when a problem occurs within the REBOL system. If you encounter one of these types of
Internal errors
errors, please report it to feedback@rebol.com.
Most types of errors can be trapped and processed by your script. See Trying Blocks for a description of the try
function.
When errors are encountered in non-interactive sessions, such as when running in CGI mode (-c or --cgi ) or in no
Windows mode (-w or --nowindow ), the session is automatically terminated.
If a script terminates while running in non-interactive mode, you can use shell redirection to output the error to a file:
7. Upgrading
On initialization, a banner is displayed that identifies the program version. Version numbers have the format:
version.revision.update.platform.variation
2.3.0.3.1
indicates that you are running version 2, revision 3, update 0, for Windows 95/98/NT (REBOL platform number 3.1). A
complete list of all platform numbers is available from http://www.rebol.com/releases.html. This HTML file also contains
a hidden REBOL database that can be used for determining the platform.
You can obtain the version number from the REBOL prompt with:
print system/version
Only the latest release of REBOL is supported by REBOL Technologies. You can verify that you have the latest
version and automatically update it if out of date. To do so, be sure that you are connected to the Internet, then from
within REBOL type:
upgrade
or:
Quick Tour
Updated: 8-Jul-2001
Table of Contents
1. Overview
2. Values
2.1. Numbers
2.2. Times
2.3. Dates
2.4. Money
2.5. Tuples
2.6. Strings
2.7. Tags
2.8. Email Addresses
2.9. URLs
2.10. Filenames
2.11. Pairs
2.12. Issues
2.13. Binary
3. Words
4. Blocks
5. Variables
6. Evaluation
7. Functions
8. Paths
9. Objects
10. Scripts
11. Files
12. Networking
12.1. HTTP
12.2. FTP
12.3. SMTP
12.4. POP
12.5. NNTP
12.6. Daytime
12.7. Whois
12.8. Finger
12.9. DNS
12.10. TCP
1. Overview
This chapter provides a quick way to familiarize yourself with the REBOL language. Using
examples, this chapter presents the basic concepts and structure of the language, illustrating
everything from data values to performing network operations.
2. Values
A script is written with a sequence of values. A wide variety of values exist and you are
familiar with many of them from daily experience.
When possible, REBOL also allows the use of international formats for values such as
decimal numbers, money, time, and date.
2.1. Numbers
Time is written in hours and minutes with optional seconds, each separated by colons. For
example:
Seconds can include a decimal sub-second. Times can also include AM and PM.
2.3. Dates
2.4. Money
2.5. Tuples
Tuples are used for version numbers, RGB color values, and network addresses They are
written as short numeric sequences separated by dots. For example:
Strings are written in a single-line format or a multiline format. Single-line-format strings are
enclosed in quotes. Multiline-format strings are enclosed in braces. Strings that include
quotes, tabs, or line breaks must be enclosed in braces using the multiline format. For
example:
2.7. Tags
Tags are useful for markup languages such as XML and HTML. Tags are enclosed in angle
brackets For example:
<title> </body>
Email addresses are written directly in REBOL. They must include an at sign (@). For
example:
info@rebol.com
pres-bill@oval.whitehouse.gov
2.9. URLs
Most types of Internet URLs are accepted directly by REBOL. They begin with a scheme
name (HTTP for example) followed by a path. For example:
http://www.rebol.com
ftp://ftp.rebol.com/sendmail.r
ftp://freda:grid@da.site.dom/dir/files/
mailto:info@rebol.com
2.10. Filenames
Filenames are preceded by a percent sign to distinguish them from other words. For
example:
%data.txt
%images/photo.jpg
%../scripts/*.r
2.11. Pairs
Pairs are used to indicate spatial coordinates, such as positions on a display. They are used
to indicate both positions and sizes. Coordinates are separated by an x. For example:
100x50
1024x800
-50x200
2.12. Issues
Issues are identification numbers, such as telephone numbers, model numbers, credit card
numbers. For example:
#707-467-8000
#0000-1234-5678-9999
#MFG-932-741-A
2.13. Binary
Binary values are byte strings of any length. They can be encoded directly as hexadecimal or
base-64. For example:
#{42652061205245424F4C}
64#{UkVCT0wgUm9ja3Mh}
3. Words
Words are the symbols used by REBOL. A word may or may not be a variable, depending on
how it is used. Words are also used directly as symbols.
REBOL has no keywords; there are no restrictions on what words are used or how they are
used. For instance, you can define your own function called print and use it instead of the
predefined function for printing values.
Words are not case sensitive and can include hyphens and a few other special characters
such as:
+ - ` * ! ~ & ? |
The end of a word is indicated by a space, a line break, or one of the following characters:
[ ] ( ) { } " : ; /
The following characters are not allowed in words:
@ # $ % ^ ,
4. Blocks
REBOL is composed by grouping values and words into blocks. Blocks are used for code,
lists, arrays, tables, directories, associations, and other sequences.
A block is enclosed in square brackets [ ]. Within a block, values and words can be organized
in any order and can span any number of lines. The following examples illustrate the valid
forms of blocks:
[
Ted ted@gw2.dom #213-555-1010
Bill billg@ms.dom #315-555-1234
Steve jobs@apl.dom #408-555-4321
]
[
"Elton John" 6894 0:55:68
"Celine Dion" 68861 0:61:35
"Pink Floyd" 46001 0:50:12
]
Blocks are used for code as well as for data, as shown in the following examples:
sites: [
http://www.rebol.com [save %reb.html data]
http://www.cnn.com [print data]
ftp://www.amiga.com [send cs@org.foo data]
]
foreach [site action ] [
data: read site
do action
]
A script file itself also is a block. Although it does not include the brackets, the block is
implied. For example, if the lines below were put in a script file:
red
green
blue
yellow
When the file is loaded, it will be a block that contains the words red, green, blue, and yellow.
It is equivalent to writing:
5. Variables
Words can be used as variables that refer to values. To define a word as a variable, follow
the word with a colon (:), then the value to which the variable refers as shown in the following
examples:.
age: 22
snack-time: 12:32
birthday: 20-Mar-1997
friends: ["John" "Paula" "Georgia"]
A variable can refer to any type of value, including functions (see Functions) and objects (see
Objects).
A variable refers to a specific value only within a defined context, such as a block, a function,
or an entire program. Outside that context the variable can refer to some other value or to no
value at all. The context of a variable can span an entire program or it can be restricted to a
particular block, function, or object. In other languages, the context of a variable is often
referred to as the scope of a variable.
6. Evaluation
Blocks are evaluated to compute their results. When a block is evaluated the values of its
variables are obtained. The following examples evaluate the variables age, snack-time,
birthday, and friends that were defined in the previous section:
print age
22
12:32
Georgia
A block can be evaluated multiple times by using a loop, as shown in the following examples:
**********
loop 20 [
wait 8:00
send friend@rebol.com read http://www.cnn.com
]
count: 1
count: 2
count: 3
The evaluation of a block returns a result. In the following examples, 5 and PM are the
results of evaluating each block:
print do [2 + 3]
In REBOL there are no special operator precedence rules for evaluating blocks. The values
and words of a block are always evaluated from first to last, as shown in the following
example:
print 2 + 3 * 10
50
Parentheses can be used to control the order of evaluation, as shown in the following
examples:
2 + (3 * 10)
32
(length? "boat") + 2
You can also evaluate a block and return each result that was computed within it. This is the
purpose of the reduce function:
reduce [1 + 2 3 + 4 5 + 6]
3 7 11
7. Functions
A function is a block with variables that are given new values each time the block is
evaluated. These variables are called the arguments of the function.
In the following example, the word sum is set to refer to a function that accepts two
arguments, a and b :
sum: func [a b] [a + b]
In the above example, func is used to define a new function. The first block in the function
describes the arguments of the function. The second block is the block of code that gets
evaluated when the function is used. In this example, the second block adds two values and
returns the result.
The next example illustrates one use of the function sum that was defined in the previous
example:
print sum 2 3
Some functions need local variables as well as arguments. To define this type of function,
use function, instead of func, as shown in the following example:
47
In the above example, the word series is an argument and the word total is a local variable
used by the function for calculation purposes.
The function argument block can contain strings to describe the purpose of a function and its
argument, as shown in the following example:
average: function [
"Return the numerical average of numbers"
series "Numbers to average"
] [total] [
total: 0
foreach value series [total: total + value]
total / (length? series)
]
These descriptive strings are kept with the function and can be viewed by asking for help
about the function, as shown below:
help average
USAGE:
AVERAGE series
DESCRIPTION:
Return the numerical average of numbers
AVERAGE is a function value.
ARGUMENTS:
series -- Numbers to average (Type: any)
8. Paths
If you are using files and URLs, then you are already familiar with the concept of paths. A
path provides a set of values that are used to navigate from one point to another. In the case
of a file, a path specifies the route through a set of directories to the location of the file. In
REBOL, the values in a path are called refinements.
A slash (/) is used to separate words and values in a path, as shown in the following
examples of a file path and a URL path:
%source/images/globe.jpg
http://www.rebol.com/examples/simple.r
Paths can also be used to select values from blocks, pick characters from strings, access
variables in objects, and refine the operation of a function, as shown in the following
examples:
The print function in next example shows the simplicity of using a path to access a mini-
database created from a few blocks:
towns: [
Hopland [
phone #555-1234
web http://www.hopland.ca.gov
]
Ukiah [
phone #555-4321
web http://www.ukiah.com
email info@ukiah.com
]
]
print towns/ukiah/web
http://www.ukiah.com
9. Objects
An object is a set of variables that have specific values in a context. Objects are used for
managing data structures and more complex behavior. The following example shows how a
bank account is set up as an object to specify its attributes and functions:
In the above example, the words name, balance, ss-number, deposit, and withdraw are local
variables of the account object. The deposit and withdraw variables are functions that are
defined within the object. The variables of the account can be accessed with a path, as
shown in the next example:
print account/balance
$100.00
account/deposit $300
The next example shows how to make another account with a new balance but with all the
other values remaining the same
You can also create an account that extends the account object by adding the bank name
and last activity date, as shown in the following example:
print checking-account/balance
$2000.00
print checking-account/bank
Savings Bank
print checking-account/last-active
20-Jun-2000
10. Scripts
A script is a file that holds a block that can be loaded and evaluated. The block can contain
code or data, and typically contains a number of sub-blocks.
Scripts require a header to identify the presence of code. The header can include the script
title, date, and other information. In the following example of a script, the first block contains
the header information:
REBOL [
Title: "Web Page Change Detector"
File: %webcheck.r
Author: "Reburu"
Date: 20-May-1999
Purpose: {
Determine if a web page has changed since it was
last checked, and if it has, send the new page
via email.
}
Category: [web email file net 2]
]
page: read http://www.rebol.com
if any [
not exists? %page-sum.r
page-sum <> (load %page-sum.r)
][
print ["Page Changed" now]
save %page-sum.r page-sum
send luke@rebol.com page
]
11. Files
In REBOL, files are easily accessed. The following table describes some of the ways to
access files.
For instance, you could write out the current time with:
do %script.r
make-dir %newdir/
print what-dir
delete %oldfile.txt
12. Networking
There are a number of Internet protocols built into REBOL. These protocols are easy to use
and require very little knowledge of networking.
12.1. HTTP
The following example shows how to use the HTTP protocol to read a web page:
The next example fetches an image from a web page and writes it to a local file:
12.2. FTP
The following reads and writes files to a server using the file transfer protocol (FTP):
12.3. SMTP
The following example sends email with the simple mail transfer protocol (SMTP):
send luke@rebol.com "Use the force."
12.4. POP
The following example fetches email with the post office protocol (POP) and prints all of the
current messages but leaves them on the server:
12.5. NNTP
The following example fetches news with the network news transfer protocol (NNTP), reading
all of the news in a particular news group:
The next example reads a list of all news group and prints them:
12.6. Daytime
12.7. Whois
The following example finds out who is in charge of a domain using the whois protocol:
12.8. Finger
The following example gets user information with the finger protocol:
12.9. DNS
The following example determines an Internet address from a domain name and a domain
name from an address:
12.10. TCP
Direct connections with TCP/IP are also possible in REBOL. The following example is a
simple, but useful, server that waits for connections on a port, then executes whatever has
been sent:
forever [
connection-port: first server-port
until [
wait connection-port
error? try [do first connection-port]
]
close connection-port
]
Chapter 4: Expressions
Updated: 7-Jul-2001
Table of Contents
1. Overview
2. Blocks
3. Values
3.1. Direct and Indirect Values
3.2. Datatypes of Values
4. Evaluating Expressions
4.1. Evaluating Console Input
4.2. Evaluating Simple Values
4.3. Evaluating Blocks
4.4. Reducing Blocks
4.5. Evaluating Scripts
4.6. Evaluating Strings
4.7. Evaluation Errors
5. Words
5.1. Word Names
5.2. Word Usage
5.3. Setting Words
5.4. Getting Words
5.5. Literal Words
5.6. Unset Words
5.7. Protecting Words
6. Conditional Evaluation
6.1. Conditional Blocks
6.2. Any and All
6.3. Conditional Loops
6.4. Common Mistakes
7. Repeated Evaluation
7.1. Loop
7.2. Repeat
7.3. For
7.4. Foreach
7.5. Forall and Forskip
7.6. Forever
7.7. Break
8. Selective Evaluation
8.1. Select
8.2. Switch
8.2.1. Default Case
8.2.2. Common Cases
8.2.3. Other Cases
9. Stopping Evaluation
10. Trying Blocks
1. Overview
The foremost goal of REBOL is to establish a standard method of communication that spans
all computer systems. REBOL provides a simple, direct means of expressing any kind of
information with optimal flexibility and minimal syntax. For example, examine the following
line:
The line looks a lot like English making it easy to compose if you are sending it and easy to
understand if you are receiving it. However, this line is actually a valid expression in REBOL,
so your computer could also understand and act on it. REBOL provides a common language
between you and your computer. In addition, if your computer sends this expression to your
stock broker's computer, which is also running REBOL, your stock broker's computer can
understand the expression and act on it. REBOL provides a common language between
computers. The line could be sent to millions of other computer systems that could also act
on it.
The expression shown in the above example may have come from your doctor typing it, or
perhaps it originated from an application that was run by your doctor. It does not matter.
What is important is that the expression can be acted upon regardless of the type of
computer, hand-held device, kiosk, or television console you are using.
The data values (numbers, strings, prices, dates, and times) in all of the expressions shown
in the previous examples are standardized valid REBOL formats. The words, however,
depend on a specific context of interpretation to convey their meaning. Words such as sell,
at, and read have different meanings in different contexts. The words are relative
expressions--their meaning is context dependent.
Expressions can be processed in one of two ways: directly by the REBOL interpreter, or
indirectly by a REBOL script. A script processed indirectly is called a dialect. The previous
examples are dialects and, therefore, are processed by a script. The following example is not
a dialect and is processed directly by the REBOL interpreter:
In this example the words send and read are functions that are processed by the REBOL
interpreter.
2. Blocks
REBOL Expressions are based on this concept: you combine values and words into blocks.
In scripts, a block is normally enclosed with square brackets [ ]. Everything within the square
brackets is part of the block. The block contents can span any number of lines, and its format
is completely freeform. The following examples show various ways of formatting block
content:
[
"Bill" billg@ms.dom #315-555-1234
"Steve" jobs@apl.dom #408-555-4321
"Ted" ted@gw2.dom #213-555-1010
]
sites: [
http://www.rebol.com [save %reb.html data]
http://www.cnn.com [print data]
ftp://www.amiga.com [send cs@org.foo data]
]
Some blocks do not require square brackets, because they are implied. For example, in a
REBOL script, there are no brackets around the entire script, however, the script content is a
block. The square brackets of an outer-block of the script are implied. The same is true for
expressions typed at the command prompt or for REBOL messages sent between computers-
-each is an implied block.
Another important aspect of blocks is that they imply additional information. Blocks group a
set of values in a particular order. That is, a block can be used as a data set as well as a
sequence. This will be described in more detail in the Series Chapter.
3. Values
REBOL provides a built-in set of values that can be expressed and exchanged between all
systems. Values are the primary elements for composing all REBOL expressions.
A directly expressed value is known as it is lexically, or literally, written. For instance, the
number 10 or the time 10:30 are directly expressed values.
An indirectly expressed value is unknown until it is evaluated. The values none, true, and
false all require words to represent them. These values are indirectly expressed because
they must be evaluated for their values to be known. This is also true of other values, such as
lists, hashes, functions, objects.
By convention, REBOL datatype words are followed by an exclamation point (!) to help make
them stand out. For example:
integer!
char!
word!
string!
The words used for datatypes are just like any other words in REBOL.
There is nothing magic about the ! used to represent them.
See the Values Appendix for a description of all the REBOL datatypes.
4. Evaluating Expressions
To evaluate an expression is to compute its value. REBOL operates by evaluating the series
of expressions constituting a script and then returning the result. Evaluating is also called
running, processing, or executing a script.
Evaluation is performed on blocks. Blocks can be typed at the console or loaded from a script
file. Either way, the process of evaluation is the same.
Any expression that can be evaluated in a script, can also be evaluated from the REBOL
prompt, providing a simple means of testing individual expressions in a script.
For example, if you type the following expression at the console prompt:
>> 1 + 2
== 3
In the example above, the console prompt (>>) and result indicator (==)
are shown to give you an idea of how they appear in the console. For
the examples that follow, the prompt and result strings are not shown.
However, you can assume that these examples can be typed into the
console to verify their results.
Since the value of directly expressed values is known, they simply return their values. For
example, if you type the following line:
10:30
the value 10:30 is returned. This is the behavior of all directly expressed values. It includes:
integer 1234
decimal 12.34
string "REBOL world!"
time 13:47:02
date 30-June-1957
tuple 199.4.80.1
money $12.49
pair 100x200
char #"A"
binary #{ab82408b}
email info@rebol.com
issue #707-467-8000
tag <IMG SRC="xray.jpg">
file %xray.jpg
url http://www.rebol.com/
block [milk bread butter]
4.3. Evaluating Blocks
Normally, blocks are not evaluated. For example, typing the following block:
[1 + 2]
[1 + 2]
do [1 + 2]
The do function returns the result of the evaluation. In the previous example, the number 3 is
returned.
If a block contains multiple expressions, only the result of the last expression is returned:
do [
1 + 2
3 + 4
]
In this example, both expressions are evaluated, but only the result of the 3 + 4 expression is
returned.
There are a number of functions such as if, loop, while, and foreach that evaluate a block
as part of their function. These functions are discussed in detail later in this chapter, but here
are a few examples:
past noon
looping
looping
looping
looping
This is important to remember: blocks are treated as data until they are explicitly evaluated
by a function. Only a function can cause them to be evaluated.
When you evaluate a block with do, only the value of its last expression is returned as a
result. However, there are times when you want the values of all the expressions in a block to
be returned. To return the results of all of the expressions in a block, use the reduce
function. In the following example, reduce is used to return the results of both expressions in
the block:
reduce [
1 + 2
3 + 4
]
[3 7]
In the above example, the block was reduced to its evaluation results. The reduce function
returns results in a block.
The reduce function is important because it enables you to create blocks of expressions that
are evaluated and passed to other functions. Reduce evaluates each expression in a block
and puts the result of that expression into a new block. That new block is returned as the
result of reduce.
Some functions, like print, use reduce as part of their operation, as shown in the following
example:
print [1 + 2 3 + 4]
3 7
The rejoin, reform, and remold functions also use reduce as part of their operation, as
shown in the following examples:
print rejoin [1 + 2 3 + 4]
37
print reform [1 + 2 3 + 4]
3 7
print remold [1 + 2 3 + 4]
[3 7]
The rejoin, reform, and remold functions are based on the join, form, and mold functions,
but reduce their blocks first.
The do function can be used to evaluate entire scripts. Normally, do evaluates a block, as
shown in the following example:
do [print "Hello!"]
Hello!
But, when do evaluates a file name instead of a block, the file will be loaded into the
interpreter as a block, then evaluated as shown in the following example:
do %script.r
The do function can be used to evaluate expressions that are found within text strings. For
example, the following expression:
do "1 + 2"
3
returns the result 3. First the string is converted to a block, then the block is evaluated.
Evaluating strings can be handy at times, but it should be done only when necessary. For
example, to create a REBOL console line processor, type the following expression:
The above expression would prompt you with => and wait for you to type a line of text. The
text would then be evaluated, and its result would be printed. (Of course, it's not really quite
this simple, because the script could have produced an error.)
Unless it is necessary, evaluating strings is not generally a good practice. Evaluating strings
is less efficient than evaluating blocks, and the context of words in a string is not known. For
example, the following expression:
do [1 + 2]
REBOL blocks can be constructed just as easily as strings, and blocks are better for
expressions that need to be evaluated.
Errors may occur for many different reasons during evaluation. For example, if you divide a
number by zero, evaluation is stopped and an error is displayed
100 / 0
size + 10
Another common error is not providing the proper values to a function in an expression:
10 + [size]
Sometimes errors are not so obvious, and you will need to experiment to determine what is
causing the error.
5. Words
Expressions are built from values and words. Words are used to represent meaning. A word
can represent an idea or it can represent a specific value.
In the previous examples in this chapter, a number of words were used within expressions
without explanation. For instance, the do, reduce, and try words are used, but not explained.
Words are evaluated somewhat differently than directly expressed values. When a word is
evaluated, its value is looked up, evaluated, and returned as a result. For example, if you
type the following word:
zero
the value 0 is returned. The word zero is predefined to be the number zero. When the word
is looked up, a zero is found and is returned.
When words like do and print are looked up, their values are found to be functions, rather
than simple values. In such cases, the function is evaluated, and the result of the function is
returned.
Words are composed of alphabetic characters, numbers, and any of the following characters:
? ! . ' + - * & | = _ ~
A word cannot begin with a number, and there are also some restrictions on words that could
be interpreted as numbers. For example, -1 and +1 are numbers, not words.
The end of a word is marked by a space, a new line, or one of the following characters:
[ ] ( ) { } " : ; /
Thus, the brackets of a block are not part of a word. For example, the following block
contains the word test :
[test]
The following characters are not allowed in words as they cause words to be misinterpreted
or to generate an error:
@ # $ % ^ ,
Words can be of any length, but words cannot extend past the end of a line:
this-is-a-very-long-word-used-as-an-example
REBOL is not case sensitive. The following words all refer to the same word:
blue
Blue
BLUE
Words can be reused. The meaning of a word is dependent on its context, so words can be
reused in different contexts. There are no keywords in REBOL. You can reuse any word,
even those that are predefined in REBOL. For instance, you can use the word if in your code
differently than the REBOL interpreter uses this word.
Words are used in two ways: as symbols or as variables. In the following block, words are
used as symbols for colors.
green
the words have no meaning other than their use as names for colors. All words used within
blocks serve as symbols until they are evaluated.
When a word is evaluated, it is used as a variable. In the previous example, the words print
and second are variables that hold native functions which perform the required processing.
A word can be written in four ways to indicate how it is to be treated, as shown in Word
Formats.
Evaluates the word. This is the most natural and common way to write words. If
word the word holds a function, it will be evaluated. Otherwise, the value of the word
will be returned.
Defines or sets the value of a word. It is given a new value. The value can be
word:
anything, including a function. See Setting Words below.
Gets the word's value, but doesn't evaluate it. This is useful for referring to
:word
functions and other data without evaluating them. See Getting Words below.
Treats the word as a symbol, but does not evaluate it. The word itself is the
'word
value.
5.3. Setting Words
age: 42
lunch-time: 12:32
birthday: 20-March-1990
town: "Dodge City"
test: %stuff.r
You can set a word to be any type of value. In the previous examples, words are defined to
be integer, time, date, string, and file values. You can also set words to be more complex
types of values. For example, the following words are set to block and function values:
In many langages words are set with an equal sign, such as:
age = 42
In REBOL words are set with a colon. The reason for this is important. It
makes the set operation on words into a single lexical value. The
representation for the set operation is atomic.
Of couse, the other reason is that the equal sign (=) is used as a
comparision operator.
Multiple words can be set at one time by cascading the word definitions. For example, each
of the following words are set to 42:
In this example, the line sets the word time to 10:30. The word time is written as a literal
(using a single quote) so that it will not be evaluated.
10 10 10
In the above example, notice that the words do not need to be quoted because they are
within a block, which is not evaluated. The print function shows that each word is set to the
integer 10.
If set is provided a block of values, each of the individual values are set to the words. In this
example, one, two, and three are set to 1, 2, and 3:
print three
1 2 3
See the Words Section in the Values Appendix for more about setting words.
drucken: :print
defines a new word, drucken (which is German for print), to refer to the same function print
does. This is possible because :print returns the function for print, but does not evaluate it.
drucken "test"
test
Both print and drucken are set to the same value, which is the function that does printing.
This can also be accomplished with the get function. When given a literal word, get returns
its value, but does not evaluate it:
stampa "test"
test
The ability to get the value of a word is also important if you want to determine what the value
is without evaluating it. For example, you can determine if a word is a native function using
the following line:
true
Here the get returns the function for if. The if function is not evaluated, but rather it is passed
to the native? function which checks if it is a native datatype. Without the colon, the if
function would be evaluated, and, because it has no arguments, an error would occur.
The ability to deal with a word as a literal is useful. Both set and get, as well as other
functions like value?, unset, protect, and unprotect, expect a literal value.
Literal words can be written in one of two ways: by prefixing the word with a single quotation
mark, also known as a tick, (`) or by placing the word in a block.
word: 'this
In the above example, the word variable is set to the literal word this, not to the value of this.
The word variable just uses the name symbolically. The example below shows that if you
print the value of the word, you will see the this word:
print word
this
You can also obtain literal words from an unevaluated block. In the following example, the
first function fetches the first word from the block. This word is then set to the word variable.
Any word can be used as a literal. It may or may not refer to a value. For example, in the
example below the word here has no value. The word print does have a value, but it can still
be used as a literal because literal words are not evaluated.
word: 'here
print word
here
word: 'print
print word
video: [
title "Independence Day"
length 2:25:24
date 4/july/1996
]
print select video 'title
Independence Day
In this example, the word title is searched for in a block. If the tick was missing from title, then
its natural value would be used. If title has no natural value, an error is displayed.
See the Words Section in the Values Appendix for more information about word literals.
A word that has no value is unset. If an unset word is evaluated, an error will occur:
>> outlook
The error message in the previous example indicates that the word has not been set to a
value. The word is unset. Do not confuse this with a word that has been set to none, which is
a valid value.
unset 'word
To determine if a word has been set, use the value? function, which takes a literal word as
its argument:
Determining whether a word is set can be useful in scripts that call other scripts. For
instance, a script may set a default parameter that was not previously set:
You can prevent a word from being set with the protect function:
protect 'word
An attempt to redefine a protected word causes an error:
word: "here"
unprotect 'word
word: "here"
Important function and system words can be protected using the protect-system function.
Protecting function and system words is especially useful for beginners who might
accidentally set important words. If protect-system is placed in your user.r file, then all
predefined words are protected.
6. Conditional Evaluation
As previously mentioned, blocks are not normally evaluated. A do function is required to
force a block to be evaluated. There are times when you may need to conditionally evaluate
a block. The following section describes several ways to do this.
The if function takes two arguments. The first argument is a condition and the second
argument is a block. If the condition is true , the block is evaluated, otherwise it is not
evaluated.
past noon
The condition is normally an expression that evaluates to true or false ; however, other
values can also be supplied. Only a false or a none value prevents the block from being
evaluated. All other values (including zero) are treated as true, and cause the block to be
evaluated. This can be useful for checking the results of find, select, next, and other
functions that return none :
found
The either function extends if to include a third argument, which is the block to evaluate if
the condition is false:
after lunch
Both the if and either functions return the result of evaluating their blocks. In the case of an
if, the block value is only returned if the block is evaluated; otherwise, a none is returned.
The if function is useful for conditional initialization of variables:
print flag
lunch eaten
Making use of the result of the either function, the previous example could be rewritten as
follows:
after lunch
Since both if and either are functions, their block arguments can be any expression that
results in a block when evaluated. In the following examples, words are used to represent the
block argument for if and either.
notice: [print "Wake up!"]
if now/time > 7:00 notice
Wake up!
notices: [
[print "It's past sunrise!"]
[print "It's past noon!"]
[print "It's past sunset!"]
]
if now/time > 12:00 second notices
Wake up!
The conditional expressions used for the first argument of both if and either can be
composed from a wide variety of comparison and logic functions. Refer to the Math Chapter
for more information.
The most commonly made mistake in REBOL is to forget the second block on either
or add a second block to if. These examples both creator hard-to-find errors:
These types of errors may be difficult to detect, so keep this in mind if these functions
do not seem to be doing what you expect.
The any and all functions offer a shortcut to evaluating some types of conditional
expressions. These functions can be used in a number of ways:either in conjunction with if,
either, and other conditional functions, or separately.
Both any and all accept a block of expressions, which is evaluated one expression at a time.
The any function returns on the first true expression, and the all function returns on the first
false expression. Keep in mind that a false expression can also be none, and that a true
expression is any value other than false or none.
The any function returns the first value that is not false, otherwise it returns none. The all
function returns the last value if all the expressions are not false, otherwise it returns none.
Both the any and all functions only evaluate as much as they need. For example, once any
has found a true expression, none of the remaining expressions are evaluated. Here is an
example of using any :
size: 50
if any [size < 10 size > 90] [
print "Size is out of range."
]
The behavior of any is also useful for setting default values. For example, the following lines
set a number to 100, but only when its value is none :
number: none
print number: any [number 100]
100
Similarly, if you have various potential values, you can use the first one that actually has a
value (is not none ):
80
You can use any with functions like find to always return a valid result:
999
Similarly, all can be used for conditions that require all expressions to be true :
You can verify that values have been set up before evaluating a function:
a: "REBOL/"
b: none
probe all [string? a string? b append a b]
none
b: "Core"
probe all [string? a string? b append a b]
REBOL/Core
The until and while functions repeat the evaluation of a block until a condition is met.
The until function repeats a block until the evaluation of the block returns true (that is, not
false or none ). The evaluation block is always evaluated at least once. The until function
returns the value of its block.
The example below will print each word in the color block. The block begins by printing the
first word of the block. It then moves to the next color for each color in the block. The tail?
function checks for the end of the block, and will return true, which will cause the until
function to exit.
red
green
blue
The break function can be used to escape from the until loop at any time.
The while function repeats the evaluation of its two block arguments while the first block
returns true. The first block is the condition block, the second block is the evaluation block.
When the condition block returns false or none, the expression block will no longer be
evaluated and the loop terminates.
Here is a similar example to that show above. The while loop will continue to print a color
while there are still colors to print.
red
green
blue
The condition block can contain any number of expressions, so long as the last expression
returns the condition. To illustrate this, the next example adds a print to the condition block.
This will print the index value of the color. It will then check for the tail of the color block,
which is the condition used for the loop.
1
red
2
green
3
blue
4
The last value of the block is returned from the while function.
Conditional expressions are only false when they return false or none, and they are true
when they return any other value. All of the conditional expressions in the following examples
return true, even the zero and empty block values:
yep
if 1 [print "yep"]
yep
if 0 [print "yep"]
yep
if [] [print "yep"]
yep
yep
Do not confuse either with if. For example, if you intend to write:
the if function would ignore the second block. This would not cause an error, but the second
block would never get evaluated.
The opposite is also true. If you write the following line, omitting a second block:
either some-condition [a: 1]
the either function will not evaluate the correct code and may produce an erroneous result.
7. Repeated Evaluation
The while and until functions above where used to loop until a condition was met. There are
also several functions that let you loop for a specified a number of times.
7.1. Loop
The loop function evaluates a block a specified number of times. The following example
prints a line of 40 dashes:
----------------------------------------
Note that the prin function is similar to the print function, but prints its argument without a
line termination.
The loop function returns the value of the final evaluation of the block:
i: 0
print loop 40 [i: i + 10]
400
7.2. Repeat
The repeat function extends loop by allowing you to monitor the loop counter. The repeat
function's first argument is a word that will be used to hold the count value:
count: 1
count: 2
count: 3
i: 0
print repeat count 10 [i: i + count]
55
In the previous examples, the count word only has its value within the repeat block. In other
words, the value of count is local to the block. After repeat finishes, count returns to any
previous set value.
7.3. For
The for function extends repeat by allowing the starting value, the ending value, and the
increment to the value to be specified. Any of the values can be positive or negative.
The example below begins at zero and counts to 50 by incrementing 10 each time through
the loop.
0
10
20
30
40
50
The for function cycles through the loop up to and including the ending value. However, if the
count exceeds the ending value, the loop is still terminated. The example below specifies an
ending value of 55. That value will never be hit because the loop increments by 10 each time.
The loop stops at 50.
0 10 20 30 40 50
The next example shows how to count down. It begins at four and counts down to zero one
at a time.
The for function also works for decimal numbers, money, times, dates, series, and
characters. Be sure that both the starting and ending values are of the same datatype. Here
are several examples of using the for loop with other datatypes.
10.5 9.5 8.5 7.5 6.5 5.5 4.5 3.5 2.5 1.5 0.5
abcdefghijklmnopqrstuvwxyz
The for function also works on series. The following example uses for on a string value. The
word end is defined as the string with its current index at the d character. The for function
moves through the string series one character at a time and stops when it reaches the
character position defined to end:
str: "abcdef"
end: find str "d"
for s str end 1 [print s]
abcdef
bcdef
cdef
def
7.4. Foreach
The foreach function provides a convenient way to repeat the evaluation of a block for each
element of a series. It works for all types of block and string series.
red
green
blue
string: "REBOL"
foreach char string [print char]
REBOL
files: read %.
foreach file files [
if find file ".t" [print file]
]
file.txt
file2.txt
newfile.txt
output.txt
When a block contains groups of values that are related, the foreach function can fetch all
the values of the group at the same time. For example, here is a block that contains a time,
string, and price. By providing the foreach function with a block of words for the group, each
of their values can be fetched and printed.
movies: [
8:30 "Contact" $4.95
10:15 "Ghostbusters" $3.25
12:45 "Matrix" $4.25
]
foreach [time title price] movies [
print ["watch" title "at" time "for" price]
]
In the above example, the foreach value block, [time title price], specifies that three values
are to be fetched from movies for each evaluation of the block.
The variables used to hold the foreach values are local to the block. Their value are only set
within the block that is being repeated. Once the loop has exited, the variables return to their
previously set values.
Similar to foreach, the forall function evaluates a block for every value in a series. However,
there are some important differences. The forall function is handed the series that is set to
the beginning of the loop. As it proceeds through the loop, forall modifies the position within
the series.
red
green
blue
In the above example, after each evaluation of the block, the series is advanced to its next
position. When forall returns, the color index is at the tail of the series.
To continue to use the series you will need to return it to its head position with the following
line:
The forskip function evaluates a block for groups of values in a series. The second
argument to forskip is the count of how many elements to move forward after each cycle of
the loop.
Like forall, forskip is handed the series with the series index set to where it is to begin.
Then, forskip modifies the index position as it continues the loop. After each evaluation of
the body block, the series index is advanced by the skip amount to its next index position.
The following example demonstrates forskip :
movies: [
8:30 "Contact" $4.95
10:15 "Ghostbusters" $3.25
12:45 "Matrix" $4.25
]
Contact
Ghostbusters
Matrix
In the above example, forskip returns with the movies series at its tail position. You will need
to use the head function to return the series back to its head position.
7.6. Forever
The forever function evaluates a block endlessly or until a it encounters the break function.
The following example uses forever to check for the existence of a file every ten minutes:
forever [
if exists? %datafile [break]
wait 0:10
]
7.7. Break
You can stop the repeated evaluation of a block with the break function. The break function
is useful when a special condition is encountered and the loop must be stopped. The break
function works with all types of loops.
In the following example, the loop will break if a number is greater than 5.
repeat count 10 [
if (random count) > 5 [break]
print "testing"
]
testing
testing
testing
The break function does not return a value from the loop unless a /return refinement is used:
testing
testing
testing
stop here
In the above example, if the repeat terminates without the condition occurring, the block
returns the string normal exit. Otherwise, break/return will return the string stop here.
8. Selective Evaluation
There are several methods to selectively evaluate expressions in REBOL. These methods
provide a way for evaluation to branch many different ways, based on a key value.
8.1. Select
The select function is often used to obtain a particular value or block, given a target value. If
you define a block of values and actions, you can use select to search for the action that
corresponds to a value.
cases: [
center [print "center"]
right [print "right"]
left [print "left"]
]
action: select cases 'right
if action [do action]
right
In the previous example, the select function finds the word right and returns the block that
follows it. (If for some reason the block was not found, then none would have been returned.)
The block is then evaluated. The values used in the example are words, but they can be any
kind of value:
cases: [
5:00 [print "everywhere"]
10:30 [print "here"]
18:45 [print "there"]
]
action: select cases 10:30
if action [do action]
here
8.2. Switch
The select function is used so often that there is a special version of it called switch, which
includes the evaluation of the resulting block. The switch function makes it easier to perform
inline selective evaluation. For instance, to switch on a simple numeric case:
switch 22 [
11 [print "here"]
22 [print "there"]
]
there
The switch function also returns the value of the block it evaluates, so the previous example
can also be written as:
print switch 22 [
11 [join str "here"]
22 [join str "there"]
]
right there
and:
car: pick [Ford Chevy Dodge] random 3
print switch car [
Ford [351 * 1.4]
Chevy [454 * 5.3]
Dodge [154 * 3.5]
]
2406.2
The cases can be any valid datatype, including numbers, strings, words, dates, times, urls,
and files. Here are some examples:
Strings:
person: "kid"
switch person [
"dad" [print "here"]
"mom" [print "there"]
"kid" [print "everywhere"]
]
everywhere
Words:
person: 'kid
switch person [
dad [print "here"]
mom [print "there"]
kid [print "everywhere"]
]
everywhere
Datatypes:
person: 123
switch type?/word [
string! [print "a string"]
binary! [print "a binary"]
integer! [print "an integer number"]
decimal! [print "a decimal number"]
]
an integer number
Files:
file: %rebol.r
switch file [
%user.r [print "here"]
%rebol.r [print "everywhere"]
%file.r [print "there"]
]
everywhere
URLs:
url: ftp://ftp.rebol.org
switch url [
http://www.rebol.com [print "here"]
http://www.cnet.com [print "there"]
ftp://ftp.rebol.org [print "everywhere"]
]
everywhere
Tags:
tag: <LI>
print switch tag [
<PRE> ["Preformatted text"]
<TITLE> ["Page title"]
<LI> ["Bulleted list item"]
]
Times:
time: 12:30
switch time [
8:00 [send wendy@domain.dom "Hey, get up"]
12:30 [send cindy@rebol.dom "Join me for lunch."]
16:00 [send group@every.dom "Dinner anyone?"]
]
8.2.1. Default Case
A default case can be specified when none of the other cases match. Use the default
refinement to specify a default:.
time: 7:00
switch/default time [
5:00 [print "everywhere"]
10:30 [print "here"]
18:45 [print "there"]
] [print "nowhere"]
nowhere
If you have common cases, where the result would be the same for several values, you can
define a word to hold a common block of code:
url: http://www.rebol.com
switch url [
http://www.rebol.com case1
http://www.cnet.com [print "there"]
ftp://ftp.rebol.org case1
]
20
More than just blocks can be evaluated for cases. This example evaluates the file that
corresponds to a day of the week:
switch now/weekday [
1 %monday.r
5 %friday.r
6 %saturday.r
]
So, if it's Friday, the friday.r file is evaluated and its result is returned from the switch. This
type of evaluation also works for URLs:
switch time [
8:30 ftp://ftp.rebol.org/wakeup.r
10:30 http://www.rebol.com/break.r
18:45 ftp://ftp.rebol.org/sleep.r
]
The cases for switch are enclosed in a block, and therefore can be defined apart from the
switch statement:
schedule: [
8:00 [send wendy@domain.dom "Hey, get up"]
12:30 [send cindy@dom.dom "Join me for lunch."]
16:00 [send group@every.dom "Dinner anyone?"]
]
9. Stopping Evaluation
Evaluation of a script can be stopped at any time by pressing the escape key (ESC) on the
keyboard or by using the halt and quit functions.
The halt function stops evaluation and returns you to the REBOL console prompt:
The quit function stops evaluation and exits the REBOL interpreter:
The try function allows you to catch errors during the evaluation of a block. It is almost
identical to do. The try function will normally return the result of the block; however, if an
error occurs, it will return an error value instead.
In the following example, when the divide by zero occurs, the script will pass an error back to
the try function, and evaluation will continue from that point.
for num 5 0 -1 [
if error? try [print 10 / num] [print "error"]
]
2
2.5
3.33333333333333
5
10
error
Scripts
Updated: 11-Jul-2001
Table of Contents
1. Overview
1.1. File Suffix
1.2. Structure
2. Headers
2.0.1. Prefaced Scripts
2.0.2. Embedded Scripts
3. Script Arguments
3.1. Program Options
4. Running Scripts
4.1. Loading Scripts
4.2. Saving Scripts
4.3. Commenting Scripts
5. Style Guide
5.1. Formatting
5.1.1. Indent Content for Clarity
5.1.2. Standard Tab Size
5.1.3. Detab Before Posting
5.1.4. Limit Line Lengths to 80 Characters
5.2. Word Names
5.2.1. Use the Shortest Word that Communicates the Meaning
5.2.2. Use Whole Words Where Possible
5.2.3. Hyphenate Multiple Word Names
5.2.4. Begin Function Names with a Verb
5.2.5. Begin Data Words with Nouns
5.2.6. Use Standard Names
5.3. Script Headers
5.4. Function Headers
5.5. Script File Names
5.6. Embedded Examples
5.7. Embedded Debugging
5.8. Minimize Globals
6. Script Cleanup
1. Overview
The term script refers not only to single files that are evaluated but also to source text
embedded within other types of files (such as, web pages), or fragments of source text that
are saved as data files or passed as messages.
REBOL scripts typically append a .r suffix to file names; however, this convention is not
required. The interpreter reads files with any suffix and scans the contents for a valid REBOL
script header.
1.2. Structure
The structure of a script is free-form. Indentation and spacing can be used to clarify the
structure and content of the script. In addition, you are encouraged to use the standard
scripting style to make scripts more universally readable. See the Style Guide for more
information.
2. Headers
Directly preceding the script body, every script must have a header that identifies its purpose
and other script attributes. A header can contain the script name, author, date, version, file
name, and additional information. REBOL data files that are not intended for direct evaluation
do not require a header.
Headers are useful for several reasons.
They identify a script as being valid source text for the REBOL interpreter.
The interpreter uses the header to print out the script's title and determine what
resources and versions it needs before evaluating the script.
Headers provide a standard way to communicate the title, purpose, author, and other
details of scripts. You can often determine from a script's header if a script interests
you.
Script archives and web sites use headers for generating script directories,
categories, and cross references.
Some text editors access and update a script's header to keep track of information
such as the author, date, version, and history.
REBOL [block]
For the interpreter to recognize the header, the block must immediately follow the word
REBOL. Only white space (spaces, tabs, and lines) is permitted between the word REBOL
and the block.
The block that follows the REBOL word is an object definition that describes the script. The
preferred minimal header is:
REBOL [
Title: "Scan Web Sites"
Date: 2-Feb-2000
File: %webscan.r
Author: "Jane Doer"
Version: 1.2.3
]
When a script is loaded, the header block is evaluated and its words are set to their defined
values. These values are used by the interpreter and can also be used by the script itself.
Note that words defined as a single value can also be defined as multiple values by providing
them in a block:
REBOL [
Title: "Scan Web Sites"
Date: 12-Nov-1997
Author: ["Ema User" "Wasa Writer"]
]
Headers can be more complex, providing information about the author, copyright, formatting,
version requirements, revision history, and more. Because the block is used to construct the
header object, it can also be extended with new information. This means that a script can
extend the header as needed, but it should be done with care to avoid ambiguous or
redundant information.
REBOL [
Title: "Full REBOL Header Example"
Date: 8-Sep-1999
Name: 'Full-Header ; For window title bar
Version: 1.1.1
File: %headfull.r
Home: http://www.rebol.com/rebex/
Purpose: {
The purpose or general reason for the program
should go here.
}
Note: {
An important comment or notes about the program
can go here.
}
History: [
0.1.0 [5-Sep-1999 "Created this example" "Carl"]
0.1.1 [8-Sep-1999 {Moved the header up, changed
comment on extending the header, added
advanced user comment.} "Carl"]
]
Language: 'English
]
Script text does not need to begin with a header. Scripts can begin with any text, allowing
them to be inserted into email messages, web pages, and other files.
The header marks the beginning of the script, and the text that follows is the body of the
script. Text that appears before the header is called the preface and is ignored during
evaluation.
REBOL [
Title: "Preface Example"
Date: 8-Jul-1999
]
If a script is to be followed by other text unrelated to the script itself, the script must be
enclosed with square brackets [ ]:
Only white space is permitted between the initial bracket and the word REBOL.
3. Script Arguments
When a script is evaluated, it has access to information about itself. This is found in the
system/script object. The object contains the fields listed in Object Fields for system/script.
Header The header object of the script. This can be used to access the
script's title, author, version, date, and other fields.
Parent If the script was evaluated from another script, this is the
system/script object for the parent script.
Path The file directory path or URL to the script being evaluated.
Args The arguments to the script. These are passed from the
operating system command line or from the do function that
was used to evaluate the script.
print system/script/title
print system/script/header/date
do system/script/args
do system/script/path/script.r
The last example evaluates a script called script.r in the same directory as the script that is
currently running.
Scripts also have access to the options provided to the REBOL interpreter when it was
started. These are found in the system/options object. The object contains the fields listed in
Object Fields for system/options.
The system/options object also contains additional options that were provided on the
command line. Type
probe system/options
Examples:
print system/options/script
probe system/options/args
4. Running Scripts
There are two ways to run a script: as the initial script when the REBOL interpreter is started,
or from the do function.
To run a script when starting the interpreter, provide the script name on the command line
following the REBOL program name:
rebol script.r
From the do function, provide the script file name or URL as an argument. The file is loaded
into the interpreter and evaluated:
do %script.r
do http://www.rebol.com/script.r
The do function returns the result of the script when it finishes evaluation.
Note that the script file must include a valid REBOL header.
Script files can be loaded as data with the load function. This function reads the script and
translates the script into values, words, and blocks, but does not evaluate the script. The
result of the load function is a block, unless only a single value was loaded, then that value is
returned.
The script argument to the load function is a file name, URL, or a string.
load %script.r
load %datafile.txt
load http://www.rebol.org/script.r
load "print now"
probe data
Note that a file does not require a header to be loaded. The header is necessary only if the
file is to be run as a script.
The load function supports a few refinements. The load Function Refinements lists the
refinements and a description of their functionality:
/next Loads only the next value, one value at a time. This is useful for
parsing REBOL scripts.
/markup Treats the file as an HTML or XML file and returns a block that
holds its tags and text.
Normally, load does not return the header from the script. But, if the /header refinement is
used the returned block contains the header object as its first argument.
The /next refinement loads the next value and returns a block containing two values. The
first returned value is the next value from the series. The second returned value is the string
position immediately following the last item loaded.
The /markup refinement loads HTML and XML data as a block of tags and strings. All tags
are tag data types. All other data are treated as strings.
<title>This is an example</title>
probe data
Data can be saved to a script file in a format that can be loaded into REBOL with the load
function. This is a useful way to save data values and blocks of data. In this fashion, it is
possible to create entire mini-databases.
The save function expects two arguments: a file name and either a block or a value to be
saved:
The data is written out in REBOL source text format, which can be loaded later with:
Simple values can also be saved and loaded. For instance, a date stamp can be saved with:
In the previous example, because stamp is a single value, it is not enclosed in a block when
loaded.
To save a script file with a header, the header can be provided in a refinement as either an
object or a block:
Commenting is useful for clarifying the purpose of sections of a script. Script headers provide
a high level description of the script and comments provide short descriptions of functions. It
is also a good idea to provide comments for other parts of your code as well.
A single-line comment is made with a semicolon. Everything following the semicolon to the
end of the line is part of the comment:
You can also use strings for comments. For instance, you can create multi-line comments
with a string enclosed in braces:
{
This is a long multilined comment.
}
This technique of commenting works only when the string is not interpreted as an argument
to a function. If you want to make sure that a multi-line comment is recognized as a comment
and is not interpreted as code, precede the string with the word comment :
comment {
This is a long multilined comment.
}
The comment function tells REBOL to ignore the following block or string. Note that string
and block comments are actually part of the script block. Care should be taken to avoid
placing them in data blocks, because they would appear as part of the data.
5. Style Guide
REBOL scripts are free-form. You can write a script using the indenting, spacing, line length,
and line terminators you prefer. You can put each word on a separate line or join them
together on one long line.
While the formatting of your script does not affect the interpreter, it does affect its human
readability. Because of this, REBOL Technologies encourages you to follow the standard
scripting style described in this section.
Of course, you don't have to follow any of these suggestions. However, scripting style is
more important than it first seems. It can make a big difference in the readability and reuse of
scripts. Users may judge the quality of your scripts by the clarity of their style. Sloppy scripts
often mean sloppy code. Experienced script writers usually find that a clean, consistent style
makes their code easier to produce, maintain, and revise.
5.1. Formatting
Use the following guidelines for formatting REBOL scripts for clarity.
The contents of a block are indented, but the block's enclosing square brackets [ ] are not.
That's because the square brackets belong to the prior level of syntax, as they define the
block but are not contents of the block. Also, it's easier to spot breaks between adjacent
blocks when the brackets stand out.
Where possible, an opening square bracket remains on the line with its associated
expression. The closing bracket can be followed by more expressions of that same level.
These same rules apply equally to parenthesis ( ) and braces { }.
if check [
do this and do that
do another thing
do a few more things
]
while [
do a longer expression
to see if it's true
][
the end of the last block
and start of the new one
are at the WHILE level
]
adder: func [
"This is an example function"
arg1 "this is the first arg"
arg2 "this is the second arg"
][
arg1 + arg2
]
An exception is made for expressions that normally belong on a single line, but extend to
multiple lines:
This also applies to grouped values that belong together, but must be wrapped to fit on the
line:
[
"Hitachi Precision Focus" $1000 10-Jul-1999
"Computers Are Us"
REBOL standard tab size is four spaces. Because people use different editors and readers
for scripts, you can elect to use spaces rather than tabs.
The tab character (ASCII 9) does not indent four spaces in many viewers, browsers, or
shells, so use an editor or REBOL to detab a script before publishing it to the net. The
following function detabs a file with standard four-space tabs:
For ease of reading and portability among editors and email readers, limit lines to 80
characters. Long lines that get wrapped in the wrong places by email clients are difficult to
read and have problems loading.
Words are a user's first exposure to your code, so it is critical to choose words carefully. A
script should be clear and concise. When possible, the words should relate to their English or
other human language equivalent, in a simple, direct way.
Local words can often be shortened to a single word. Longer, more descriptive words are
better for global words.
What you save when abbreviating a word is rarely worth it. Type date not dt, or image-file not
imgfl.
The standard style is to use hyphens, not character case, to distinguish words.
Function names begin with a verb and are followed by a noun, adverb, or adjective. Some
nouns can also be used as verbs.
When using a noun as a verb, use special characters such as ? where applicable. For
instance, the function for getting the length of a series is length?. Other REBOL functions
using this naming convention are:
There are standard names in REBOL that should be used for similar types of operations. For
instance:
The advantage of using headers is clear. Headers give users a summary of a script and
allow other scripts to process the information (like a cataloging script). A minimum header
provides a title, date, file name and purpose. Other fields can also be provided such as
author, notes, usage, and needs.
REBOL [
Title: "Local Area Defringer"
Date: 1-Jun-1957
File: %defringe.r
Purpose: {
Stabilize the wide area ignition transcriber
using a double ganged defringing algorithm.
}
]
It is useful to provide a description in function specification blocks. Limit such text to one line
of 70 characters or less. Within the description, mention what type of value the function
normally returns.
defringe: func [
"Return the defringed localization radius."
area "Topo area to defringe"
time "Time allotted for operation"
/cost num "Maximum cost permitted"
/compound "Compound the calculation"
][
...code...
]
The best way to name a file is to think about how you can best find that file in a few months.
Short and clear names are often enough. Plurals should be avoided, unless meaningful.
In addition, when naming a script, consider how the name will sort in a directory. For
instance, keep related files together by starting them with a common word.
%net-start.r
%net-stop.r
%net-run.r
Where appropriate, provide examples within a script to show how the script operates and to
give users a quick way of verifying that the script works correctly on their system.
It is often useful to build in debugging functions as part of the script. This is especially true of
networking and file handling scripts where it is not desirable to send and write files while
running in test mode. Such tests can be enabled with a control variable at the head of the
script.
verbose: on
check-data: off
5.8. Minimize Globals
In large scripts and where possible, avoid using global variables that carry their internal state
from one module or function to another. For short scripts, this isn't always practical. But
recognize that short scripts may become longer scripts over time.
If you have a collection of global variables that are closely related, consider using an object
to keep track of them:
6. Script Cleanup
Here is a short script that can be used to clean up the indentation of a script. It works by
parsing the REBOL syntax and reconstructing each line of the script. This example can be
found in the REBOL Script Library at www.REBOL.com.
Basic Concepts
Series Functions
Series Data Types
Series Information
Making and Copying Series
Series Iteration
Searching Series
Sorting Series
Series as Data Sets
Multiple Series Variables
Modification Refinements
Basic Concepts
The concept of a series is quite simple to grasp and is the basis for nearly everything in REBOL. It is
very important to understand series well.
There are many series data types in REBOL. A block, a string, a list, a URL, a path, an email, a file, a
tag, a binary, a bitset, a port, a hash, an issue, and an image are all series data types. Series data types
can all be accessed and processed in the same way with the same small set of functions.
Traversing a Series
Since a series is an ordered set of values, you can traverse within it. As an example, take a series of
three colors defined by the following block:
There is nothing special about this block. It is a series containing three words. It has a set of values:
red, green, and blue. The values are organized into a specific order: red is first, green is second, and
blue is third.
The first position of the block is called its head . This is the position occupied by the word red . The
last position of the block is called its tail. This is the position immediately after the last word in the
block. If you were to draw a diagram of the block, it would look like this:
Notice that the tail is just past the end of the block. The importance of this will become more clear
shortly.
The variable colors is used to refer to the block. It is currently set to the head of the block:
true
red
green
You can reposition the colors variable in the block using various functions. To move the colors
variable to the next position in the colors block, use the next function:
The next function moves forward one value in the block and returns that position as a result. The
colors variable is now set to that new position:
The position of the colors variable has changed. Now the variable is no longer at the head of the
block:
false
green
The position of the value that is returned by the first function is relative to the position that colors
has in the block. The returned value is not the first color in the block, but the first color immediately
following the current position of the block.
Similarly, if you ask for the length or the second color, you find that these are relative as well:
blue
You could move to the next position, and get a similar set of results:
blue
The colors variable is now at the last color in the block, but it is not yet to the tail position.
false
Now the colors variable is resting at the tail of the block. It is no longer positioned at a valid color,
but is past the end of the block.
If you try your code, you will get:
false
You receive an error in this last case because there is no valid first item when you are past the end of
the block.
you will move the colors variable back one position in the series:
blue
Skipping Around
The previous examples move through the series one item at a time. However, there are times when
you want to skip past multiple items using the skip function. Assume that the colors variable is
positioned at the head of a series:
The skip function is similar to next in that skip returns the series at the new position.
blue
green
Note that you cannot skip past the tail or the head of a series. If you attempt to do so, skip only goes
as far as it can. It will not generate an error.
If you skip too far forward, skip returns the tail of the series:
true
If you skip too far back, skip returns the head of the series:
true
To skip directly to the head of the series, use the head function:
true
red
You can return to the tail with the tail function:
true
Extracting Values
Some of the previous examples made use of the first and second ordinal functions to extract
specific values from a series. The full set of ordinal functions is:
first
second
third
fourth
fifth
last
Ordinal functions are provided as a convenience, and are used for picking values from the most
common position in a series. Here are some examples:
red
blue
indigo
blue
indigo
print colors/3
blue
print colors/5
indigo
Remember, as shown earlier, extraction is performed relative to the series variable that you
provide. If the colors variable were at another position in the series, the results would be different.
Extracting a value past the end of its series generates an error in the case of the ordinal functions and
returns none in the case of the pick function or a pick path:
none
print colors/10
none
Extracting a Sub-series
You can extract multiple values from a series with the copy function. To do so, use copy with the
/part refinement, which specifies the number of values that you want to extract:
probe sub-colors
[red green]
To copy a sub-series from any position within the series, first traverse to the starting position. The
following example moves forward to the second position in the series using next before performing
the copy:
probe sub-colors
[green blue]
The length of the series to copy can be specified as an ending position, as well as a copy count. Note
that the position indicates where the copy should stop, not the ending position.
[red]
[red green]
probe copy/part next colors back tail colors
[green]
This can be useful when the ending position is found as the result of the find function:
file: %image.jpg
image
You can insert one or more new values into any part of a series using the insert function. When you
insert a value at a position in a series, space is made by shifting its prior values toward the tail of the
series.
To insert a new value at the head of the block where the colors variable is now positioned:
The red and green words are shifted over and the blue word (which is prefixed with a tick
because it is a word and should not be evaluated) is inserted at the head of the list.
Note that the colors variable remains positioned at the head of the list.
probe colors
Also note that the return from the insert function was not used because it was not set to a variable or
passed along to another function. If the return had been used to set the value of the colors variable
with the line:
the effect on the block would have been the same, but the position of the colors variable would
have changed as a result of setting the return value. The position returned from insert is immediately
following the insertion point.
An insertion can be made anywhere in the series. The position of the insert can be specified, and it can
include the tail. Inserting at the tail has the effect of appending to the series.
probe colors
Another way to insert at the tail of a series is with the append function. The append function works
in the same way as insert , but always inserts at the tail. The previous example would become:
The insert and append function also accept a block of arguments to insert. As an example:
probe colors
If you want to insert the new values between the red and green words:
probe colors
The insert and append functions have other capabilities that are covered in more detail in a later
section.
Removing Values
You can remove one or more values from any part of a series by using the remove function.
As shown here:
You can remove the first value from the block with the line:
remove colors
probe colors
The remove function removes values relative to the position of the colors variable. You can
remove values from anywhere in the series by setting the position.
Similar to insert/part , the argument to remove/part can also be a position within the block.
Removing all of the remaining values is a common operation. The clear function is provided to make
this more direct. Clear removes all values from the current position to the tail. For example:
As shown here:
clear colors
Changing Values
One additional set of functions is provided for changing values in a series. The change function
replaces one or more values with new values. Although this can be accomplished by removing and
inserting values, it is more efficient to use change .
probe colors
The poke function allows you to specify that the change occur at a particular position relative to the
colors variable. The poke function is similar to the pick function described earlier.
probe colors
The change function has additional refinements that are described later in this chapter.
Series Functions
Here is a summary of the functions that operate on series. Most of these were described in detail in the
previous section. Others will be covered in more detail in this section.
Creation
Creation Functions
Function Description
make Makes a new series of the given type.
Navigation
Navigation Functions
Function Description
next Returns the next position in a series.
at Returns the position plus or minus an integer, but uses the same indexing as pick .
Information
Information Functions
Function Description
head ? Returns true if at the head of the series.
Extraction
Extraction Functions
Function Description
pick Extracts a single value from a position in a series.
Modification
Modification Functions
Function Description
insert Inserts values into a series.
Search
Search Functions
Function Description
find Finds a value in a series.
Ordering
Ordering Functions
Function Description
sort Sorts the values in a series into an order.
Data Sets
Data Set Functions
Function Description
unique Returns a unique set of values, removing duplicates.
Block Types
Block Types
String Types
Pseudo-types
Series data types are grouped into a few pseudo-types that make function argument and type testing
easier:
Pseudo-types
Pseudo-type Description
Series! A series data type
Any-block! Any of the block data types
Series Information
Length?
The length of a series is the number of items (values for a block or characters for a string) from the
current position to the tail . If the current position is the head of the series, then the length is the
number of items in the entire series.
print length? []
data: [1 2 3 4 5 6 7 8]
8
data: next data
Head?
The head of a series is the position of its first value. If a series is at its head, the head? function
returns true:
data: [1 2 3 4 5]
true
false
Tail?
The tail of a series is the position immediately following the last value. If a series variable is
at the tail, the tail? function returns true:
data: [1 2 3 4 5]
false
true
If empty? returns true , it means there are no values between the current position and the tail;
however, there still may be values in the series. Values can still be present before the current position.
If you need to determine if the series is empty from head to tail, use:
false
Index?
The index is the position in a series relative to the head of the series. To determine the index
position for a series variable, use the index? function:
data: [1 2 3 4 5]
Offset?
The distance between two positions in a series can be determined with the offset? function.
data: [1 2 3 4]
In this example, the offset is the difference between position 2 and position 4:
Empty?
Use the make function to create a new series from a series data type and an initial size. The size is an
estimate of the size needed for the series. If the initial size is too small, the series will automatically
expand, but at a slight performance cost.
block: copy [1 2 3 4 5]
new-block: copy block
Copying is also important for use with functions that modify the contents of a series. For instance, if
you want to change the case of a string without modifying the original, use the copy :
Partial Copies
The copy function /part refinement takes a single argument, which is either an integer specifying the
number of items to copy or a position within the series indicating the last position to copy.
print str
Message in a bottle
Message
print new-str
in a
probe new-blk
Deep Copies
Many blocks contain other blocks and strings. When such a block is copied, its sub-series are not
copied. The sub-series are referred to directly and are the same series data as the original block. If you
modify any of these sub-series, you modify them in the original block as well.
The copy/deep refinement forces a copy of all series values within a block:
blk-one: ["abc" [1 2 3]]
probe blk-one
["abc" [1 2 3]]
probe blk-one
probe blk-two
["abc" [1 2 3]]
["abc" [1 2 3]]
If either the string or block contained in blk-two is modified, the series values in blk-one are also
modified.
append blk-two/2 [4 5 6]
probe blk-one
probe blk-two
["abcDEF" [1 2 3 4 5 6]]
["abcDEF" [1 2 3 4 5 6]]
Using copy/deep makes a copy of all series values found in the block:
append blk-two/2 [7 8 9]
probe blk-one
probe blk-two
["abcDEF" [1 2 3 4 5 6]]
["abcDEFghi" [1 2 3 4 5 6 7 8 9]]
Initial Copies
When initializing a string or block series, use copy on the value to make is a unique series:
blk: copy []
Using copy assures that a new series is created for the word every time the word is initialized. Here is
an example of why this is important.
str: ""
print str
print-it
ha
print-it
haha
print-it
hahaha
In this example, because copy wasn't used, the empty string series is modified with every call of
print-it . The string series ha is inserted into str each time print-it is called.
Examining the source of the function as it now exists exposes the root of the problem:
source print-it
Although str is a local variable, its string value is global. To avoid this problem, the function should
copy the empty string or use make on the string.
print str
print-it
ha
print-it
ha
print-it
ha
Series Iteration
You can use a loop to traverse a series. There are a few loop functions that can help automate the
iteration process.
While Loop
The most flexible approach is to use a while loop, which allows you to do just about anything to the
series without problems.
The method shown below allows you to insert values without hitting a value twice:
if colors/1 = 'yellow [
This example illustrates that the insert returns the position immediately following the insertion.
To remove a value without accidentally skipping a value, use the following code:
remove colors
][
Forall Loop
The forall loop is similar to the while loop, but eliminates some of the effort required. The forall loop
starts from the current index and advances through a series to its tail evaluating a block for every
value.
The forall loop takes two arguments: a series variable and a block to evaluate for each iteration.
red
green
blue
yellow
orange
The forall advances the variable position through the series, so when it returns the variable is left at
its tail:
true
Also, if the block modifies the series, be careful to avoid missing or repeating a value. The forall loop
works in some cases; however, if you are uncertain, use the while loop instead.
forall colors [
red
green
yellow
orange
Forskip Loop
Similar to forall , the forskip loop advances through a series starting at the current position, but skips
the specified number of values each time.
The forskip loop takes three arguments: a series variable, the skip between each iteration, and a block
to evaluate for each iteration.
red
blue
orange
The forskip loop leaves the series at its tail, requiring you to reset it.
true
Foreach Loop
The foreach loop moves through a series setting a word or multiple words in to the values in the
series.
The foreach loop takes three arguments: a word or a block of words that holds the values for each
iteration, a series, and a block to evaluate for each iteration.
red
green
blue
yellow
orange
gold
red green
blue yellow
orange gold
foreach [c1 c2 c3] colors [print [c1 c2 c3]]
The foreach loop does not advance the current index through the series, so there is no need to reset its
series variable.
Any of the loops can be stopped at any time by evaluating the break function from within the
evaluation block. See the Expressions Chapter for more information about the break function.
Searching Series
The find function searches through block or string series for a value or pattern. This function has
many refinements that permit a wide range of variations in search parameters.
Simple Find
The simplest and most common use of find is to search a block or string for a value. In this case, find
requires only two arguments: the series to search and the value to find.
probe where
blue
The find function can also search for values by data type. This can be quite useful.
20-Feb-2000
United
print where
none
Refinement Summary
Find has many refinements that support a wide variety of search parameters:
Refinement Summary
Refinement Description
/ part Limits a search on a series to a given length or ending position.
Allows pattern wildcards with different characters other than asterisk (*) and (?). This
/ with
allows a pattern to contain asterisks and question marks.
Matches a pattern beginning at the current series position, rather than finding the first
/ match
occurrence of a value or string. Returns the tail position if the match is found.
Return the tail position of a match on a successful search, rather than returning the
/ tail
point at which the match was found.
/ last Searches backwards for the match, starting at the tail of the series.
/ reverse Searches backwards for the match, starting at the current position.
Partial Searches
The /part refinement allows a search to be confined to a specific portion of a series. For instance, you
may want to restrict a search to a given line or section of text.
Similar to insert/part and remove/part , find/part takes either a count or an ending position. The
following example uses a count and restricts the search to the first three items:
The next example uses an ending position. The search is restricted to a single line of text:
text: {
print item
line one.
Tail Positions
The find function returns the position in the series where an item was found. The /tail refinement
returns the position immediately following the item that was found. Here's an example:
filename: %script.txt
.txt
txt
print filename
script.r
Backward Searches
The last example in the previous section would fail if the filename had more than one period. For
instance:
filename: %new.script.txt
.script.txt
In this example we want the last occurrence of the period in the string, which can be found using the
/last refinement. The /last refinement searches backward through a series.
.txt
txt
If you want to continue to search backward through the string, you need the /reverse refinement. This
refinement performs a search from the current position backward toward the head, rather than forward
toward the tail.
print where
.txt
.script.txt
Notice that /reverse continues the search just before the position of the last match. This prevents it
from finding the same period again.
Repeated Searches
You can easily repeat the find function to search for multiple occurrences of a value or string. Here is
an example that would print all the strings found in a block:
The next example counts the number of new lines in a script. It uses the /tail refinement to prevent an
infinite loop and returns the position immediately following the match.
count: 0
To perform a repeated search in reverse, use the /reverse refinement. The following example prints all
of the index positions in reverse order for the text of a script.
Matching
The /match refinement modifies the behavior of find to perform pattern matching on the current
position of a series. This refinement allows parsing operations to be performed by matching the next
part of a series with expected patterns. See the chapter on Parsing for another way to match series.
none
Notice in the example that a search is not performed. The beginning of the series either matches or it
does not. If it does match, the series is advanced the position immediately following the match point,
allowing you to match the next sequence.
grammar: [
true
]
print parse-it "Keep things simple"
true
true
false
The capability of /match can be greatly extended with the addition of the /any refinement as
discussed below.
Wildcard Searches
The /any refinement enables wildcard pattern matching. The question mark (?) and asterisk (*)
characters act as wildcards for matching any single character or any number of characters
respectively. The /any refinement can be used in conjunction with find with or without the /match
refinement.
Examples:
str: "abcdefg"
cdefg
bcdefg
email-list: [
mack@rebol.dom
judy@somesite.dom
jack@rebol.dom
biff@rebol.dom
jenn@somesite.dom
mack@rebol.dom
jack@rebol.dom
biff@rebol.dom
The next example uses the /match refinement to attempt to match the pattern to the next part of the
series:
file-list: [
%rebol.exe
%notes.html
%setup.html
%feedback.r
%nntp.r
%rebdoc.r
%rebol.r
%user.r
rebdoc.r
rebol.r
If either of the wildcard characters are part of what is to be matched, substitute wildcard characters
can be provided using the /with refinement.
Select
A useful variation of the find function is the select function, which returns the value following the one
found. The select function is often used to lookup a value in tagged blocks of data. The select function
takes the same arguments as find : the series to search and the value find. However, unlike find ,
which returns a series position, the select function returns the value that follows the match.
blue
Given a simple database, the select function can be used to access its values:
email-book: [
"George" harrison@guru.org
"Paul" lefty@bass.edu
"Ringo" richard@starkey.dom
"Robert" service@yukon.dom
lefty@bass.edu
Use the select function to find a block of expressions to evaluate. For example, given the following
data:
cases: [
10 [print "ten"]
20 [print "twenty"]
30 [print "thirty"]
]
do select cases 10
ten
do select cases 30
thirty
To replace values throughout a series, you can use the replace function. This function searches for a
specific value in a series, then replaces it with a new value.
The replace function takes three arguments: the series to search, value to replace, and the new value.
data: [1 2 8 4 5]
[1 2 3 4 5]
[1 2 3 four 5]
[0 2 3 four 5]
Use the /all refinement to replace all occurrences of the value from the current position to the tail.
probe code
do code
hello
world
Sorting Series
The sort function offers a simple, quick method of sorting series. It is most useful for blocks of data,
but can also be used on strings of characters.
Simple Sorting
abellmops
Notice that sort is destructive to its argument series. It reorders the original data. To prevent this, use
copy , as in the following example:
0123456789AbCdEfGhI
ABCDEFGHIabcdefghi
0123456789ABCDEFGHIabcdefghi
Group Sorting
Often it is necessary to sort a data set that has more than one value per record. The /skip refinement
supports this for sorting records that have a fixed length. The refinement takes one additional
argument: an integer specifying length of each record.
Here is an example that sorts a block that contains first name, last name, ages, and emails. The block
is sorted by its first column, first-name.
names: [
sort/skip names 4
Comparison Functions
The /compare refinement allows you to perform custom comparisons on the data being sorted. This
refinement takes an additional argument, which is the comparison function to use for ordering the
data.
A comparison function is written as a regular function that takes two arguments. These arguments are
the values to be compared. A comparison function returns true if the first value should be placed
before the second value and false if the first value should be placed after the second value.
If the first value is less than the second, then true is returned from the function and the first value is
placed before the second value.
Similarly:
If the first value is greater than the second value, then true is returned and the data is sorted with
greater values first. The sort will descend from greater values.
Notice that in both cases the comparison function is passed by providing its name preceded with a
colon. The name preceded with a colon causes the function to be passed to sort without first being
evaluated. The comparison function could also be provided directly with:
Unique
The unique function returns a unique set that contains no duplicate values.
Examples:
abrcd
Intersect
The intersect function takes two series and returns a series that contains the values that are present in
both series.
Examples:
[Bob]
[ham carrot]
print intersect [1 3 2 4] [3 5 4 6]
34
CD
all-chars: "ABCDEFGHI"
true
false
[bill Bob]
Union
The union function takes two series and returns a series that contains all the values from both series,
but no duplicates.
Examples:
print union [1 3 2 4] [3 5 4 6]
132456
ABCDEF
true
true
Exclude
The exclude function takes two series and returns a series that contains all the values of the first
series, less the values of the second.
probe exclude [1 2 3 4] [1 2 3 5]
[4]
[Bill Bart]
[cheese bread]
AB
[Bill bob]
Difference
The difference function takes two series and returns a series that contains all of the values not in
common with both series.
Examples:
probe difference [1 2 3 4] [1 2 3 5]
[4 5]
probe difference [Bill Bob Bart] [Bob Ted Fred]
ABEF
data: [1 2 3 4 5]
Both the start and end variables refer to the series. They have different positions, but the series
they reference is the same.
If an insert or remove function is performed on a series, the values in the series will shift and the
start and end variables may no longer refer to the same values. For instance, if a value is removed
from the series at the start position:
remove start
The series has shifted to the left and the variables now refer to different values.
Notice that the index positions of the variables have not changed, but the values in the series have
changed. The same situation would occur when using insert .
Sometimes this side effect will work to your advantage. Sometimes it will not, and you will need to
correct for it in your code.
Modification Refinements
The change , insert , and remove functions can take additional refinements to modify their operation.
Part
The /part refinement accepts a count or a position in the series and uses it to limit the effect of the
function.
str: "abcdef"
blk: [1 2 3 4 5 6]
change/part str [1 2 3 4] 3
probe str
1234def
probe blk
["abcd" 4 5 6]
You can insert part of a series into the tail of str and blk using insert/part .
probe str
1234def-ghi
probe blk
["abcd" 4 5 6 "--" 7 8 9]
To remove part of the str and blk series, use remove/part . Note how find is used to obtain the
series position:
probe str
1234-ghi
probe blk
["abcd" "--" 7 8 9]
Only
The /only refinement changes or inserts a block as a block, rather than its individual values.
Examples:
blk: [1 2 3 4 5 6]
You can replace the 2 in blk with the block [a b c] and insert the block [$1 $2 $3] at the
position of the 5 .
probe blk
[1 [a b c] 3 4 5 6]
probe blk
Dup
Examples:
str: "abcdefghi"
blk: [1 2 3 4 5 6]
You can change the first four values in a string or block series to an asterisk (*) with:
change/dup str "*" 4
probe str
****efghi
probe blk
To insert a dash (-) four times before the last value in a string or block:
probe str
****efgh----i
probe blk
WWW.REBOL.COM
REBOL/Core Users Guide
Block Series
Updated: 13-Jul-2001
Table of Contents
1. Blocks of Blocks
2. Paths for Nested Blocks
3. Arrays
3.1. Creating Arrays
3.2. Initial Values
4. Composing Blocks
1. Blocks of Blocks
When a block appears as a value within another block, it counts as a single value regardless
of how many values it contains. For example:
values: [
"new" [1 2]
%file1.txt ["one" ["two" %file2.txt]]
]
probe values
The length? of values is four. The second and fourth values are counted as single values:
print length? values
The block values within the values block can be used as a block as well. In the following
examples, second is used to extract the second value from values.
[1 2]
block
In the same way, series operations can be performed on other types of series values in
blocks. In the following examples, pick is used to extract %file1.txt from values.
%file1.txt
The fourth value in values is a block containing another block. The following examples use a
path to get information about this value.
probe values/4
probe values/4/2
["two" %file2.txt]
block
block
The two series values in the fourth value's block can also be accessed.
To look at the values, type:
probe values/4/2/1
two
probe values/4/2/2
%file2.txt
string
file
too
%file2.r
The above examples illustrate REBOL's ability to operate on values nested inside blocks.
Note that in the last series of examples, change is used to modify a string and file series
three layers deep in values. Printing out the values block produces:
probe values
3. Arrays
arr: [
[1 2 3 ]
[a b c ]
[$10 $20 $30]
]
You can obtain the values of an array with the series extraction functions:
[1 2 3]
You can also use paths to obtain values from the array:
probe arr/1
[1 2 3]
probe arr/3
probe arr/3/2
$20.00
arr/1/2: 20
probe arr/1 == [1 20 3]
The array function creates arrays dynamically. The function takes an argument that is either
an integer or a block of integers and returns a block that is the array. By default, the cells of
an array are initialized to none. To initialize array cells to some other value, use the /initial
refinement explained in the next section.
When array is supplied with a single integer, a one-dimensional array of that size is returned:
arr: array 5
probe arr
When a block of integers is provided, the array has multiple dimensions. Each integer
provides the size of the corresponding dimension.
Here is an example of a two dimensional array that has six cells, two rows of three columns:
arr: array [2 3]
probe arr
arr: array [2 3 2]
foreach lst arr [probe lst]
The block of integers that is passed to array can be as big as your memory will support.
To initialize the cells of an array to a value other than none, use the /initial refinement. This
refinement takes one argument: the initial value. Here are some examples:
arr: array/initial 5 0
probe arr
[0 0 0 0 0]
arr: array/initial [2 3] 0
probe arr
[[0 0 0] [0 0 0]]
The compose function takes a block as an argument and returns a block that has each value
in the argument block. Values in parentheses are evaluated before the block is returned. For
example:
[1 2 7]
If the values in parentheses return a block, that block's individual values are used:
[a b c d]
[a b [c d]]
[a b c d]
When compose is given a block that contains sub-blocks, the sub-blocks are not evaluated,
even if they contain parentheses:
[a b [c (d e)]]
If you would like the sub-blocks to be evaluated, use the /deep refinement. The /deep
refinement causes all parentheses to be evaluated, regardless of where they are:
[a b [c d e]]
String Series
Updated: 13-Jul-2001
Table of Contents
1. String Functions
2. Converting Values to Strings
2.1. Join
2.2. Rejoin
2.3. Form
2.4. Reform
2.5. Mold
2.6. Remold
2.7. String Spacing Functions
2.7.1. Trim
2.7.2. Detab and Entab
2.8. Uppercase and Lowercase
2.9. Checksum
2.10. Compression and Decompression
2.11. Number Base Conversion
2.12. Internet Hexadecimal Decoding
1. String Functions
There are a wide variety of functions that operate on or produce strings. Functions are available
for modifying strings, searching strings, compressing and decompressing strings, changing the
spacing of strings, parsing strings, and converting strings. These functions operate on all string
related datatypes, such as string!, binary!, tag!, file!, URL!, email!, and issue!.
The string creation, modification and search functions are covered in the Series chapter. They
include the items listed in String Functions.
In addition, the series traversing functions like next, back, head, and tail were covered. They
are used to reposition in strings. In addition, the series test functions allow you to determine your
position within a string.
This chapter will introduce functions that convert REBOL values into strings. These functions are
used often, and they are also used by the print and probe functions. They include:
2.1. Join
The join function takes two arguments and concatenates them into a single series.
The data type of series returned is based on the value of the first argument. When the first
argument is a series value, that series type is returned.
str: "abc"
file: %file
url: http://www.rebol.com/
abc123
%file.txt
http://www.rebol.com/index.html
When the first argument is not a series, the join converts it to a string first, then performs the
append:
$11.00 dollars
30-Jun-2000 -- today
255.255.255.0 netmask
When the second argument to join is a block, the values of that block are evaluated and
appended to the series returned.
abc12
%/dir1/sub-dir/filename.txt
11:09:11AM on 30-Jun-2000
312.423123987234
2.2. Rejoin
The rejoin function is identical to join, except that it takes one argument, a block.
try123
2.3. Form
$1.50
money
string
The following example uses form to find a number by its decimal value:
44.11
11.11
When form is used on a block, all values in the block are converted to string values with spaces
between each value:
The form function does not evaluate the values of a block. This results in words being converted
to string values:
The reform function is like form, except that blocks are reduced before being converted.
2.5. Mold
The mold function converts a value to a string that is usable by REBOL. Strings created with
mold can be converted back to values with the load function.
block
string
[11 * 4]
block
[11 * 4]
money: $11.11
sub-blk: [inside another block mold this is unevaluated]
probe mold [$22.22 money "-- unevaluated block:" sub-blk]
2.6. Remold
The remold function works just like mold, except that blocks are reduced before being
converted.
The default operation of trim is to remove extra spaces from the head and tail of a string:
print str
Trim includes a number of refinements to specify where space is to be removed from a string:
/auto removes space from each line, relative to the first line
Use the /head and /tail refinements to trim from either end of a string:
Use the /auto refinement to trim leading spaces from multiple lines leaving indented spaces
intact:
str: {
indent text
indent text
indent text
indent text
indent text
}
print str
indent text
indent text
indent text
indent text
indent text
{indent text
indent text
indent text
indent text
indent text
}
Use /lines to trim the head and tail and also convert newlines into spaces:
{indent text indent text indent text indent text indent text}
indenttextindenttextindenttextindenttextindenttext
The /with refinement will remove all characters that you specify. In the following example,
spaces, line breaks and the characters e and t are removed:
The detab and entab will convert tabs to spaces and spaces to tabs.
str:
{^(tab)line one
^(tab)^(tab)line two
^(tab)^(tab)^(tab)line three
^(tab)line^(tab)full^(tab)of^(tab)tabs}
print str
line one
line two
line three
line full of tabs
By default, the detab function converts tabs to four spaces (the REBOL standard spacing). All
tabs in the string will be converted to spaces, regardless of where they are located.
{ line one
line two
line three
line full of tabs}
Note that the detab and entab functions affect the string that is provided as an argument. To
change a copy of the source string, use the copy function.
The entab function converts spaces to tabs. Every four spaces will be converted to a single tab.
Only spaces at the beginning of a line will be converted to tabs.
{^-line one
^-^-line two
^-^-^-line three
^-line^-full^-of^-tabs}
You can use the /size refinement to specify the size of tabs. For instance, if you want to convert
each tab to eight spaces, or convert every eight spaces to a tab, you can use this example:
{ line one
line two
line three
line full of tabs}
{^-line one
^-^-line two
^-^-^-line three
^-line^-full^-of^-tabs}
There are two functions for changing character casing: uppercase and lowercase. The
uppercase function takes a string argument and converts its characters to uppercase:
Ukiah
2.9. Checksum
The checksum returns the checksum of the string value. There are three types of checksum
that can be computed:
CRC 24 bit circular redundancy checksum
52719
356358
10943
A secure checksum will return a binary value, not an integer. Use the /secure refinement to
compute a secure checksum:
#{AAF4C61DDCC5E8A2DABEDE0F3B482CD9AEA9434D}
The compress function will compress a string and return a binary datatype. In the following
example, a small file is compressed by reading its contents, compressing them, then writing it
back to disk:
Str:
{I wanted the gold, and I sought it,
I scrabbled and mucked like a slave.
Was it famine or scurvy -- I fought it;
I hurled my youth into a grave.
I wanted the gold, and I got it --
Came out with a fortune last fall, --
Yet somehow life's not what I thought it,
And somehow the gold isn't all.}
print [size? str "bytes"]
306 bytes
156 bytes
To be sent as text, binary strings must be converted to hexadecimal or base64 encoding. This is
often done for email and newsgroup content.
binary
probe b-line
#{4E6F2120546865726527732061206C616E6421}
The /base refinement may be used with enbase and debase to specify a base2 (binary),
base16 (hexadecimal), or base64 encoding.
01100001
binary
probe b2-str
#{61}
4E6F2120546865726527732061206C616E6421
b16-line: debase/base e16-line 16
print type? b16-line
binary
probe b16-line
#{4E6F2120546865726527732061206C616E6421}
The dehex function converts Internet URL and CGI style hexadecimal encoded characters to
strings. Hexadecimal ASCII representations appear in a URL or CGI string as %xx, where xx is
the hexadecimal value.
str: "there%20seem%20to%20be%20no%20spaces"
print dehex str
hello
Functions
Updated: 16-Jul-2001
Table of Contents
1. Overview
2. Evaluating Functions
2.1. Arguments
2.2. Argument Data Types
2.3. Refinements
2.4. Function Values
3. Defining Functions
3.1. Interface Specifications
3.2. Literal Arguments
3.3. Get Arguments
3.4. Defining Refinements
3.5. Local Variables
3.6. Local Variables Containing Series
3.7. Returning a Value
3.8. Returning Multiple Values
4. Nested Functions
5. Unnamed Functions
6. Conditional Functions
7. Function Attributes
8. Forward References
9. Scope of Variables
10. Reflective Properties
11. Online Function Help
12. Viewing Source Code
1. Overview
2. Evaluating Functions
The Expressions Chapter covered the general details of evaluation. The way function arguments are
evaluated dictates the general order of words and values in the language. This section goes into more detail
on how functions are evaluated.
2.1. Arguments
Functions receive arguments and return results. Most functions require one or more arguments; although,
some functions, such as now (current date and time), do not require any arguments.
The arguments that are supplied to a function are processed by the interpreter and then passed to the
function. Arguments are processed in the same way, regardless of the type of function called, be it a native
function, operator, user-defined function, or otherwise. For example, the send function expects two
arguments:
friend: luke@rebol.com
message: "message in a bottle"
The word friend is first evaluated and its value (luke@rebol.com ) is provided as the first argument to send.
Next, the word message is evaluated, and its value becomes the second argument. Think of the values of
the friend and message variables as being substituted into the line before send is done:
If you provide too few arguments to a function, an error message is returned. For example, the send function
expects two arguments and if you send one, an error is returned
send friend
In the previous example, send already has two arguments, so the string, which is the third argument, is
ignored. Notice that no error message occurs. In this case, there were no functions expecting the third
argument. However, in some cases the third argument may belong to another function that was evaluated
before send.
Arguments to a function are evaluated from left to right. This order is followed even when the arguments
themselves are functions. For example, if you write:
the second argument must be computed by evaluating the detab function and the copy function. The result
of the copy will be passed to detab, and the result of detab will be passed to send. In the previous example,
the copy function is taking a single argument, the message, and returns a copy of it. The copied message is
passed to the detab function, which removes the tab characters and returns the detabbed message, which is
passed to the send function. Notice how the results of functions flow from right to left as the expression is
evaluated.
The evaluation that is happening here can be shown by using parentheses to clarify what is evaluated first.
(However, the parentheses are not required, and actually slow down the evaluation slightly.)
The cascading effect of results passed to functions is quite useful. Here is an example that uses insert twice
within the same expression:
file: %image
insert tail insert file %graphics/ %.jpg
print file
graphics/image.jpg
In the following example, a directory name and a suffix are added to the base file name. Parentheses can be
used to clarify the order of evaluation:
Parentheses make good "training wheels" to get started in writing REBOL. However,
it won't take long before you can shed this aid and write the expressions directly
without the parentheses. Not using parentheses lets the interpreter evaluate
expressions quicker.
Functions usually require arguments of a specific data type. For example, the first argument to the send
function can only be an email address or block of email addresses. Any other type of value will produce an
error:
In the previous example, the error message is telling you that the address argument of the send function
needs to be either an email address or a block.
A quick way to find out what types of arguments are accepted by a function is to type the following at the
console prompt:
help send
USAGE:
SEND address message /only /header header-obj
DESCRIPTION:
Send a message to an address (or block of addresses)
SEND is a function value.
ARGUMENTS:
address -- An address or block of addresses (Type: email block)
message -- Text of message. First line is subject. (Type: any)
REFINEMENTS:
/only -- Send only one message to multiple addresses
/header -- Supply your own custom header
header-obj -- The header to use (Type: object)
The ARGUMENTS section indicates the data type of each argument. Notice that the second argument can
be of any data type. So, it is valid to write:
2.3. Refinements
A refinement specifies a variation in the normal evaluation of a function. Refinements also allow optional
arguments to be provided. Refinements are available for both native and user-defined functions.
Refinements are specified by following the function name with a forward slash (/) and a refinement name. For
instance:
You have seen the copy function used to make a copy of a string. By default, copy returns a copy of its
argument:
no time
In the previous example, the /part refinement specifies that only seven characters of the string are copied.
To review what refinements are allowed on a function such as copy, use online help:
help copy
USAGE:
COPY value /part range /deep
DESCRIPTION:
Returns a copy of a value.
COPY is an action value.
ARGUMENTS:
value -- Usually a series (Type: series port bitset)
REFINEMENTS:
/part -- Limits to a given length or position.
range -- (Type: number series port)
/deep -- Also copies series values within the block.
Notice that the /part refinement requires an additional argument. Not all refinements require additional
arguments. For example, the /deep refinement specifies that copy make copies of all its sub-blocks. No
other arguments are required.
When multiple refinements are used with a function, the order of the extra arguments is determined by the
order in which the refinements are specified. For example:
str: "test"
insert/dup/part str "this one" 4 5
print str
this this this this test
Reversing the order of the /dup and /part refinement changes the order of the arguments. You can see the
difference:
str: "test"
insert/part/dup str "this one" 4 5
print str
thisthisthisthisthistest
The previous examples describe how functions return values when they are evaluated. Sometimes, however,
you want to obtain the function as a value, not the value it returns. This can be done by preceding the
function name with a colon or using the get function. For example, to set a word, pr, to the print function, you
would write:
pr: :print
pr "this is a test"
this is a test
3. Defining Functions
You can define functions that work in the same way as native functions. These are called user-defined
functions. User-defined functions are of the function! data type.
You can make simple functions that require no arguments with the does function. This example defines a
new function that prints the current time:
10:30
The does function returns a value, which is the new function. In the example, the print-time word is set to the
function. However, this function value can be set to a word, passed to another function, returned as the result
of a function, saved in a block, or immediately evaluated.
Functions that require arguments are made with the func function, which accepts two arguments:
The first argument is a block that specifies the interface to the function. It includes a description of the
function, its arguments, the types allowed for arguments, descriptions of the arguments, and other items. The
second argument is a block of code that is evaluated whenever the function is evaluated.
The newly defined function accepts two arguments, as specified in the first block. The second block is the
body of the function, which, when evaluated, adds the two arguments together. The new function is returned
as a value from func and the sum word is set to it. Here it is in use:
444
Func is a function that makes other functions. It performs a make on the function! data type. Func is
defined as:
The first block of a function definition is called its interface specification. This block includes a description of
the function, its arguments, the data types allowed for arguments, descriptions of the arguments, and other
items.
The interface specification is a dialect of REBOL (because it has different evaluation rules than normal code).
The specification block has the format:
[
"function description"
[optional-attributes]
argument-1 [optional-type]
"argument description"
argument-2 [optional-type]
"argument description"
...
/refinement
"refinement description"
refinement-argument-1 [optional-type]
"refinement argument description"
...
]
Description A short description of the function. This is a string that can be accessed by
other functions such as help to output descriptions of functions.
Attributes A block that describes special properties of the function, such as its behavior
on errors. It may be expanded in the future to include flags for optimizations.
Argument A variable that is used to access an argument from within the body of the
function.
Arg Type A block that identifies the data types that are accepted by the function. If a data
type not identified in this block is passed to the function, an error will occur.
Arg A short description of the argument. Like the function description, this can be
Description accessed by other functions such as help.
Refinement A refinement word that indicates special behavior is required of the function.
As an example, the argument block of the sum function (defined in a previous example) is expanded to
restrict the type of arguments accepted. It also includes a description of the function and its expected
arguments.
sum: func [
"Return the sum of two numbers."
arg1 [number!] "first number"
arg2 [number!] "second number"
][
arg1 + arg2
]
Now, the data type of the arguments is automatically checked, catching errors like:
print sum 1 "test"
To allow additional argument data types, more than one can be given:
sum: func [
"Return the sum of two numbers."
arg1 [number! tuple! money!] "first number"
arg2 [number! tuple! money!] "second number"
][
arg1 + arg2
]
4.4.4
$1334.00
Now the sum function accepts a number, tuple, or monetary value as arguments. If within the function you
need to distinguish what data type was passed, you can use the data type test functions:
Because the sum function provided description strings, the help function now supplies useful information
about it:
help sum
USAGE:
SUM arg1 arg2
DESCRIPTION:
Return the sum of two numbers.
SUM is a function value.
ARGUMENTS:
arg1 -- first number (Type: number tuple money)
arg2 -- second number (Type: number tuple money)
As described earlier, the interpreter evaluates the arguments of functions and passes them to the function
body. However, there are times when you do not want function arguments evaluated. For instance, if you
need to pass a word and access it from the function body, you do not want it evaluated as an argument. The
help function, which expects a word, is a good example:
help print
To prevent print from being evaluated, the help function must specify that its argument should not be
evaluated.
To specify that an argument not be evaluated, precede the argument name with a single quote (indicates a
literal word). For example:
test: 10
zap test
print test
10
The var argument is preceded with a single quote, which instructs the interpreter to obtain the argument
without evaluating it first. The argument is passed as the word. For example:
test
Another example is a function that increments a variable by one and returns its result (similar to the ++
increment function in C):
count: 0
++ count
print count
print ++ count
Function arguments can also specify that a word's value be fetched but not evaluated. This is similar to the
literal arguments described above, but rather than passing the word, the value of the word is passed without
being evaluated.
To specify that an argument be fetched but not evaluated, precede the argument name with a colon. For
example, the following function accepts functions as arguments:
The sample function prints the body of a function that is passed to it. The argument is preceded by a colon,
which indicates that the value of the word should be obtained, but not further evaluated.
print-body reform
print-body rejoin
[
if empty? block: reduce block [return block]
append either series? first block [copy first block] [
form first block] next block
]
Refinements can be used to specify variation in the normal evaluation of a function as well as provide
optional arguments. Refinements are added to the function specification block as a word preceded by a
forward slash (/).
Within the body of the function, the refinement word is used as a logic value to determine if the refinement
was provided when the function was called.
For example, the following code adds a refinement to the sum function, which was defined in a previous
example:
sum: func [
"Return the sum of two numbers."
arg1 [number!] "first number"
arg2 [number!] "second number"
/average "return the average of the numbers"
][
either average [arg1 + arg2 / 2][arg1 + arg2]
]
The sum function specifies the /average refinement. In the body of the function, the word is tested with the
either function, which returns true when the refinement is specified.
222
To specify a refinement that accepts additional arguments, follow the refinement with the arguments
definitions:
sum: func [
"Return the sum of two numbers."
arg1 [number!] "first number"
arg2 [number!] "second number"
/times "multiply the result"
amount [number!] "how many times"
][
either times [arg1 + arg2 * amount][arg1 + arg2]
]
The amount is only valid when the times refinement is true. Here is an example:
4440
Do not forget to check the refinement word before using the additional arguments. If a refinement argument
is used without the refinement being specified, it will have a none value.
A local variable is a word whose value is defined within the scope of a function. Changes to a local variable
only affect the function in which the variable is defined. If the same word is used outside of the function, it will
not be affected by the changes to the local variable of the same name.
Argument variables and refinements are local variables. Their values are defined within the scope of the
function. By convention, additional local variables can be specified with the /local refinement. The /local
refinement is followed by a list of words that are used as local variables within the function.
average: func [
block "Block of numbers"
/local total length
][
total: 0
length: length? block
foreach num block [total: total + num]
either length > 0 [total / length][0]
]
Here the total and length words are local to the function.
Another method of creating local words is to use the function function, which is identical to func, but accepts
a separate block that contains the local words:
average: function [
block "Block of numbers"
][
total length
][
total: 0
length: length? block
foreach num block [total: total + num]
either length > 0 [total / length][0]
]
In this example, notice that the /local refinement is not used with the function function. The function
function creates the refinements for you.
If a local variable is used before its value has been set within the body of its function, it will have a none
value.
Local variables that hold series need to be copied if the series is used multiple times. For example, if you
want the stars string to be the same each time you call the start-name function, you should write:
you will be using the same string each time and each time the function is used the pervious name will appear
within the result.
*test*
*thistest*
This is Important
The concept described above is important to remember. If you forget it, you will
observe odd results in your programs.
do [1 + 3 5 + 7]
12
This is also true for functions. The last value is returned as the value of the function:
sum: func [a b] [
print a
print b
a + b
]
123
321
444
In addition, the return function can be used to stop the evaluation of a function at any point and return a
value:
probe find-value [1 2 3 4] 3
[3 4]
In the example, if the value is found, the function returns the series at the position of the match. Otherwise,
the function returns none.
To stop a function evaluation without returning a value, use the exit function:
source: func [
"Print the source code for a word"
'word [word!]
][
prin join word ": "
if not value? word [print "undefined" exit]
either any [
native? get word op? get word action? get word
][
print ["native" mold third get word]
][print mold get word]
]
To return more than one value from a function, use a block. You can do this easily by returning a block that
has been reduced.
For example:
The function returns a block that holds the series and the index value where the value was found.
probe find-value [1 2 3 4] 3
[[3 4] 3]
The reduce is necessary to create a block of values from the block of words that it is given. Do not return the
local variables themselves. That is not a supported mode of operation (currently).
To easily set variables to the return value of the function, use set:
3 4
print index
4. Nested Functions
Functions can define other functions. The sub-functions can be global, local, or returned as a result,
depending on their purpose.
For example, to create a global function from within a function, assign it to a global variable:
make-timer: func [code] [
timer: func [time] code
]
make-timer [wait time]
timer 5
The timer function only exists during the period when the do-timer function is being evaluated.
You should avoid using variables that are local to the top level function as an
unevaluated part of the nested function. For example:
In the example, the delay word dynamically belongs to the make-timer function. This
should be avoided, as the delay value will change in subsequent calls to make-timer.
5. Unnamed Functions
Function names are variables. In REBOL, a variable is a variable, regardless of what it holds. There is
nothing special about function variables.
Furthermore, functions do not require names. You can create a function and immediately evaluate it, store it
in a block, pass it as an argument to a function, or return it as a result from a function. Such functions are
unnamed.
110
print funcs/5 10
510
Functions can also be created and passed to other functions. For instance, when you use sort with your own
comparison, you provide a function as an argument:
6. Conditional Functions
Because functions are created dynamically by evaluation, you can determine how you want a function
created, based on other information. This is a way to provide conditional code as is found in the macro or
preprocessor sub-languages of other programming languages. Within the REBOL language this type of
conditional code is done with normal REBOL code.
For instance, you may want to create a debugging version of a function that prints additional information:
test-mode: on
Here you will create one of two functions, based on the test-mode you are running. This can also be written
shorter as:
Error messages typically are displayed when they occur within the function. If the catch attribute is specified,
errors that are thrown within the function are caught automatically by the function. The errors are not
displayed within the function but at the point where the function was used. This is useful if you are providing
a function library (mezzanine functions) and don't want the error to be displayed within your function, but
where it was called:
root 4
root -4
Notice that in this example, the error occurs where root was called even though the actual error was
generated in the body of the function. This is because the catch attribute was used.
Without the catch attribute, the error would occur within the root function:
The user may not know anything about the internals of the root function. So the error message would be
confusing. The user only knows about root, but the error was in square-root.
Do not get the catch attribute mixed up with the catch function. Although they are similar, the catch function
can be applied to any block that is evaluated.
The throw attribute allows you to write your own control functions, such as for, foreach, if, loop, and
forever, by allowing your functions to pass the return and exit operations. For example, this loop function:
evaluates a block until a specific time has been reached or passed. This loop can then be used within a
function:
Now, what happens when the [return none] block is evaluated? Because this block is evaluated by the loop-
time function, the return occurs in that function, not in do-job.
The throw attribute causes a return or exit that has occurred within the block to be thrown up to the
previous level, which is the next function causing do-job to return.
8. Forward References
Sometimes a script needs to refer to a function before it has been defined. This can be done as long as the
variable for the function is not evaluated before it is defined.
9. Scope of Variables
The context of variables is called their scope. The broad scope of variables is that of global and local.
REBOL uses a form of static scoping, which is called definitional scoping. The scope of a variable is
determined when its context is defined. In the case of a function, it is determined by when the function is
defined.
All of the local variables defined within a function are scoped relative to that function. Nested functions and
objects are able to access their parent's words.
a: 11
b: 10
a: 11
21
Note here that the b-func has access to the a-func variable.
Words that are bound outside of a function maintain those bindings even when evaluated within a function.
This is the result of static scoping, and it allows you to write your own block evaluation functions (like if,
while, loop ).
For example, here is a signed if function that evaluates one of three blocks based on the sign of a conditional
value:
ifs: func [
"If positive do block 1, zero do block 2, minus do 3"
condition block1 block2 block3
][
if positive? condition [return do block1]
if negative? condition [return do block3]
return do block2
]
night
The blocks passed may contain the same words used within the ifs function without interfering with the words
defined local to the function. This is because the words passed to the function are not bound to the function.
The next example passes the words block1, block2 and block3 to ifs as pre-defined words. The ifs function
does not get confused between the words passed as arguments and the words of the same name defined
locally:
block1: "morning right now"
block2: "just turned noon"
block3: "evening time"
evening time
[
"If condition is TRUE, evaluates the block."
condition
then-block [block!]
/else "If not true, evaluate this block"
else-block [block!]
]
[
head either only [
insert/only tail series :value
][
insert tail series :value
]
]
Functions can be dynamically queried during evaluation. This is how the help and source functions work and
how errors messages are formatted.
In addition, this feature is useful for creating your own unique versions of existing functions. For example, a
user-defined print function can be created that has exactly the same specification as print, but sends its
output to a string rather than the display:
[
"Outputs a value followed by a line break."
value "The value to print"
]
Useful information about all functions of the system can be retrieved with the help function:
help send
USAGE:
SEND address message /only /header header-obj
DESCRIPTION:
Send a message to an address (or block of addresses)
SEND is a function value.
ARGUMENTS:
address -- An address or block of addresses (Type: email block)
message -- Text of message. First line is subject. (Type: any)
REFINEMENTS:
/only -- Send only one message to multiple addresses
/header -- Supply your own custom header
header-obj -- The header to use (Type: object)
All of this information comes from the definition of the function. Help can be obtained for all types of
functions, not just natives or built-in functions. The help function can also be used for user-defined functions.
The documentation that is displayed about a function is provided when the function is defined.
You can also search for help on functions that contain various patterns. For instance, at the command
prompt, you could type
Help "path"
To view a list of all functions available in REBOL, type what at the command prompt.
what
* [value1 value2]
** [number exponent]
+ [value1 value2]
- [value1 value2]
/ [value1 value2]
// [value1 value2]
< [value1 value2]
<= [value1 value2]
<> [value1 value2]
= [value1 value2]
== [value1 value2]
=? [value1 value2]
> [value1 value2]
>= [value1 value2]
? ['word]
?? ['name]
about []
abs [value]
absolute [value]
...
source source
source: func [
"Prints the source code for a word."
'word [word!]
][
prin join word ": "
if not value? word [print "undefined" exit]
either any [native? get word op? get word action? get word] [
print ["native" mold third get word]
] [print mold get word]
]
Here the source function is used to print its own source code.
Note that you cannot see the source code for native functions because they exist only as machine code.
However, the source function will display the native function interface specification. For example:
source add
add: native [
"Returns the result of adding two values."
value1 [number! pair! char! money! date! time! tuple!]
value2 [number! pair! char! money! date! time! tuple!]
]
Objects
Updated: 17-Jul-2001
Table of Contents
1. Overview
2. Making Objects
3. Cloning Objects
4. Accessing Objects
5. Object Functions
6. Prototype Objects
7. Referring to Self
8. Encapsulation
9. Reflective Properties
1. Overview
Objects group values into a common context. An object can include scalar values, series,
functions, and other objects. Objects are useful in dealing with complex structures as they
allow related data and code to be encapsulated and passed as a single value to functions.
2. Making Objects
New objects are created with the make function. The make function requires two arguments
and returns a new object. The format of the make function is:
The first argument, parent-object , is the parent object from which the new object is made. If
no parent object is available, as when defining an initial object, use the object! data type, as
shown below:
The second argument, new-values , is a block that defines additional variables and initial
values for the new object. Each variable that is defined within the block is an instance
variable of the object. For example, if the block contained two variable definitions, then they
would be variables of the object:
The example object has two variables that hold two integers.
The block is evaluated, so it can include any type of expression to compute the values of the
variables:
Once an object has been made, it can serve as a prototype for creating new objects:
The above example makes a second instance of the example object. The new object is a
clone of the first object. New values for the second object are set in the block:
In the example above, the example2 object has different values than the original example
object for two of its variables.
The example2 object can also extend the object definition by adding new variables to it:
The result is an object that has five variables: Three that came from the original object,
example , and two new ones.
The process of extending the definition of an object can be repeated any number of times.
You can also create an object that contains variables that are initialized to some common
value. This can be done using a cascaded set of word definitions:
In the example above, the four variables are set to none within the new object.
Use make to create a new object based on a parent object or the object! data type.
Add any new variables that are defined in the block to the new object.
Evaluate the block, which causes the variables defined in the block to be set to the
values in the new object.
The new object is returned as a result.
3. Cloning Objects
When you use a parent object to make a new object, the parent object is cloned rather than
inherited. This means that if the parent object is modified, it has no effect on the child object.
As an example, the following code creates a bank account object, whose variables are blank:
To use the new object, values can be provided to create an account for a customer:
Since new accounts are made on a regular basis, it helps to use a function and some global
variables to create them:
last-account: 89431
bank-bonus: $10.00
make-account: func [
"Returns a new account object"
f-name [string!] "First name"
l-name [string!] "Last name"
start-balance [money!] "Starting balance"
][
last-account: last-account + 1
make bank-account [
first-name: f-name
last-name: l-name
account: last-account
balance: start-balance + bank-bonus
]
]
4. Accessing Objects
Variables within objects are accessed with paths. The path consists of the object name
followed by the name of the variable. For example, the following code accesses the variables
in the example object:
example/var1
example/var2
print luke/last-name
Lakeswimmer
print fred/balance
$510.00
fred/balance: $1000.00
print fred/balance
$1000.00
You can use the in function to access object variables by fetching their words from within
their object context:
balance
The balance word returned has the object fred as its context. You can get the value it holds
by using get:
$1000.00
The second argument to the in function is a literal word. This allows you to dynamically
change words depending on what is needed:
FredSmith
$1000.00
Each word in the block is used to obtain its value in the object.
$20.00
If a word is not defined within an object, the in function returns none . This is useful for
detecting when a variable exists within an object.
5. Object Functions
An object can contain variables that refer to functions that are defined within the context of
the object. This is useful because the functions are encapsulated within the context of the
object, and can access the other variables of the object directly, without a need for a path.
As a simple example, the example object can include functions for computing new values
within the object:
Notice in the example that the functions are able to refer to the variables of the object
directly, rather than as paths. That is possible because the functions are defined within the
same context as the variables they access.
This example evaluates the function that sets var3 to the current time.
example/calculate 100
print example/var2
110
In the case of the bank-account object, the functions for deposit and withdraw can be added
to the current definition:
In the example, notice that the functions are able to refer to the balance directly within the
object. That's because the functions are part of the object's context.
Now if a new account is made, it will contain functions for depositing and withdrawing money.
For example:
print lily/balance
$1010.00
lily/deposit $100
print lily/balance
$1110.00
lily/withdraw $2000
print lily/balance
-$890.00
lily/withdraw $2.10
6. Prototype Objects
Any object can serve as a prototype for making new objects. For instance, the lily account
object previously defined can be used to make new objects with a line such as:
This makes an instance of an object. The object is a copy of the customer object and has
identical values:
print lily/balance
-$890.00
print maya/balance
-$890.00
You can modify the new object while making it by providing the new values within the
definition block:
print maya/balance
$10000.00
maya/deposit $500
print maya/balance
$10500.00
print maya/first-name
Maya
The lily object serves as a prototype for creating the new object. Any words that are not
redefined for the new object continue to have the values of the old object:
print maya/last-name
Lakeswimmer
7. Referring to Self
Every object includes a predefined variable called self . Within the context of an object, the
self variable refers to the object itself. It can be used to pass the object to other functions or
to return it as a result of a function.
In the following example, the show-date function requires an object as its argument and self
is passed to it:
example/show
16-Jul-2000/11:08:37-7:00
Another example of using the self variable is a function that clones itself:
print lulu/days-old
7366
8. Encapsulation
An object provides a good way to encapsulate a group of variables that should not appear at
the global level. When function variables are defined as globals, they can unintentionally be
modified by other functions.
The solution to this problem of global variables is to wrap an object around both the variables
and the function. When that is done, the function can still access the variables, but the
variables cannot be accessed globally. For example:
last-account: 89431
bank-bonus: $10.00
In this example, the variables are safe from accidental modification. Notice that the make-
account function was set to a variable using the set function, rather than using a variable
definition. This was done to make it a global function. The function can be used in the same
way as functions set with a variable definition, but does not require an object path:
9. Reflective Properties
As with many other REBOL data types, you can access the components of objects in a
manner that allows you to write useful tools and utilities for creating, monitoring, and
debugging them.
The first and second functions allow you to access the components of an object. The first
function returns the words defined for an object. The second function returns the values that
the objects are set to. The following diagram shows the relationship between the return
values of first and second:
The advantage to using first is that it allows you to obtain a list of the words for the function
without knowing anything else about the function:
In the above example, notice that the list contains the word, self , which is a reference to the
object itself. You can exclude self when getting an object's word list by using next:
probe next first luke
Now you have a way to write a function that can probe the contents of an object:
probe-object fred
first-name: Luke
last-name: Lakeswimmer
account: 89431
balance: $1204.52
When accessing objects in this fashion, care should be taken to avoid infinite loops. For
instance, if you attempt to probe certain objects that contain references to themselves, your
code may begin an endless loop. This is the reason why you cannot probe the system object
directly. The system object contains many references to itself.
Overview
Scalar Data Types
Evaluation Order
Standard Functions and Operators
Type Conversion
Comparison Functions
Logarithmic Functions
Trigonometric Functions
Logic Functions
Errors
Overview
REBOL provides a comprehensive set of mathematical and trigonometric operations. Many of these
operators can handle multiple datatypes, including integer, decimal, money, tuple, time, and date.
Some of these datatypes may even be mixed, or coerced.
The following are a few examples that show a range of math operations over the scalar data types.
Notice that operators produce useful results for each data type.
print 2 + 1
print 2 - 1
print 2 * 10
20
print 20 / 10
print 21 // 10
print 2.2 + 1
3.2
print 2.2 - 1
1.2
print 2.2 * 10
22
print 2.2 / 10
0.22
print random 10
4:00
print 2:20 + 5
2:20:05
print 2:20 + 60
2:21
2:20:02.2
1:00
print 2:20 - 5
2:19:55
2:18
print 2:20 * 2
4:40
print 2:20 / 2
1:10
print 2:20:01 / 2
1:10:00.5
print 2:21 // 2
0:00
print - 2:20
-2:20
5:30:52
print 1-Jan-2000 + 1
2-Jan-2000
print 1-Jan-2000 - 1
31-Dec-1999
print 1-Jan-2000 + 31
1-Feb-2000
1-Jan-2001
birthday: 7-Dec-1944
29-Apr-1695
print $2.20 + $1
$3.20
print $2.20 + 1
$3.20
$3.30
print $2.20 - $1
$1.20
print $2.20 * 3
$6.60
print $2.20 / 2
$1.10
print $2.21 // 2
$0.21
$6.00
The pair data type:
110x220
print 10x10 + 3
13x13
20x80
print 100x100 * 3
300x300
10x10
print 100x30 / 10
10x3
1x2
print 101x32 // 10
1x2
67x12
0.2.2
print 1.2.3 * 3
3.6.9
print 10.20.30 / 10
1.2.3
print 11.22.33 // 10
1.2.3
1.4.9
1.1.1
print 1.2.3 + 7
8.9.10
print 1.2.3 - 1
0.1.2
8.18.12
Evaluation Order
There are two rules to remember when evaluating mathematical expressions:
The evaluation of expressions from left to right is independent of the type of operator that is used. For
example:
print 1 + 2 * 3
In the example above, notice that the result is not seven, as would be the case if multiplication took
precedence over addition.
If you need to evaluate in some other order, reorder the expression or use parentheses:
print 2 * 3 + 1
print 1 + (2 * 3)
When functions are mixed with operators, the operators are evaluated first, then the functions:
In the above example, the addition is performed first, and its result is provided to the absolute
function.
print 10 + sine 30 + 60
11
30 + 60 => 90
sine 90 => 1
10 + 1 => 11
To change the order such that the sine of 30 is done first, use parentheses:
70.5
print 10 + 60 + sine 30
70.5
absolute
absolute
value
value
10
1.2
$1.20
print absolute -10:20
10:20
10x20
add
value1
value2
add
value1
value2
Works with integer , decimal , money , time , tuple , pair , date , char data types.
print 1 + 2
4.6
4.6.8
print $1 + $2
$3.00
5:00
40x60
print #"A" + 10
print add 1 2
complement
complement
value
print complement 10
-11
-11
155.155.155
divide
value1
value2
divide
value1
value2
Works with integer , decimal , money , time , tuple , pair , char data types.
print 10 / 2
print 1.2 / 3
0.4
print 11.22.33 / 10
1.2.3
print $12.34 / 2
$6.17
print 1:20 / 2
0:40
print 10x20 / 2
5x10
print divide 10 2
5
multiply
value1
*
value2
multiply
value1
value2
Works with integer , decimal , money , time , tuple , pair , char data types.
print 10 * 2
20
4.08
3.8.15
print $10 * 2
$20.00
print 1:20 * 3
4:00
print 10x20 * 3
30x60
print multiply 10 2
20
negate
value
negate
value
Works with integer , decimal , money , time , pair , char data types.
print - 10
-10
print - 1.2
-1.2
print - $10
-$10.00
print - 1:20
-1:20
print - 10x20
-10x-20
print negate 10
-10
random
random
value
Note that for integers random begins at 1, not 0, and is inclusive of the value given. This allows
random to be used directly with functions like pick .
When a decimal is used the result is a decimal data type rounded to an integer.
The /seed refinement restarts the random generator. Use the /seed refinement with random first it if
you want unique random number generation. You can use the current date and time to make the seed
more random:
random/seed now
Works with integer , decimal , money , time , tuple , pair , date , char data types.
print random 10
79.95.66
$32.00
6:37:33
2x4
remainder
value1
//
value2
remainder
value1
value2
Works with integer , decimal , money , time , tuple , pair data types.
print 11 // 2
print 11.22.33 // 10
1.2.3
print 11x22 // 2
1x0
print remainder 11 2
subtract
value1
-
value2
subtract
value1
value2
Works with integer , decimal , money , time , tuple , pair , date , char data types.
print 2 - 1
2.2
2.2.2
print $2 - $1
$1.00
2:20
20x20
print #"Z" - 1
print subtract 2 1
1
Type Conversion
When math operations are performed between data types, normally the non-integer or non-decimal
data type is returned. When integers are combined with decimals, a decimal data type is returned.
Comparison Functions
All comparison functions return either true or false .
equal
value1
=
value2
equal?
value1
value2
Works with integer , decimal , money , time , date , tuple , char and series data types.
true
true
true
greater
value1
>
value2
greater?
value1
value2
Returns true if the first value is greater than the second value.
Works with integer , decimal , money , time , date , tuple , char and series data types.
true
true
true
true
greater-or-equal
value1
>=
value2
greater-or-equal?
value1
value2
Returns true if the first value is greater than or equal to the second value.
Works with integer , decimal , money , time , date , tuple , char and series data types.
true
true
true
print greater-or-equal? [b c d e] [a b c d]
true
lesser
value1
<
value2
lesser?
value1
value2
Works with integer , decimal , money , time , date , tuple , char and series data types:
print 25 < 50
true
true
true
lesser-or-equal
value1
<=
value2
lesser-or-equal?
value1 value2
Returns true if the first value is less than or equal to the second value.
Works with integer , decimal , money , time , date , tuple , char and series data types.
print 25 <= 25
true
true
true
true
not equal to
value1
<>
value2
not-equal?
value1
value2
Returns true if the first and second values are not equal.
Works with integer , decimal , money , time , date , tuple , char and series data types.
print 26 <> 25
true
true
true
true
same
value1
=?
value2
same?
value1
value2
Returns true if two words refer to the same value. For instance, when you want to see if two words
are referencing the same index in a series.
Work with all data types.
reference-one: "abcdef"
reference-two: reference-one
true
false
true
false
strict-equal
value1
==
value2
strict-equal?
value1
value2
Returns true if the first and second values are strictly the same. Can be used as a case-sensitive
version of the equal? ( = ) operator for strings and to differentiate between integers and decimals
when their values are the same.
false
true
true
false
true
true
strict-not-equal
strict-not-equal?
value1
value2
Returns true if the first and second values are strictly not the same. Can be used as a case-sensitive
version of the not-equal? ( <> ) operator for strings and to differentiate between integers and
decimals when their values are the same.
true
false
true
false
false
Logarithmic Functions
exp
exp
value
log-10
log-10
value
log-2
log-2
value
log-e
log-e
value
power
value1
**
value2
power
value1
value2
square-root
square-root
value
Trigonometric Functions
The trigonometric functions deal in degrees. Use the /radians refinement with any of the
trigonometric functions to operate in and return radians.
arccosine
arccosine
value
arcsine
arcsine
value
Returns trigonometric arcsine of value .
arctangent
arctangent
value
cosine
cosine
value
sine
sine
value
tangent
tangent
value
Logic Functions
Logic functions can be performed on logic values and on some scalar values including integer , char ,
tuple , and bitset. When working with logic values, the logic functions return boolean values. When
working with other types of values, the logic functions on the bits.
and
The and function compares two logic values and returns true if they are both true :
print (1 < 2) and (2 < 3)
true
false
When used with integers, the and function compares bit for bit and returns 1 if both bits are 1 , or 0 if
neither bit is 1 :
print 3 and 5
or
The or function compares two logic values and returns true if either of them are true or false or if
both are false :
true
true
false
When used with integers, or compares bit for bit and returns 1 if either bit is 1 or 0 if both bits are 0:
print 3 or 5
xor
The xor function compares two logic values and returns true if and only if one of the values is true
and the other is false.
print (1 < 2) xor (2 < 3)
false
true
false
When used with integers, xor compares bit for bit and returns 1 if and only if one bit is 1 and the
other 0 . Otherwise, it returns 0 :
print 3 xor 5
complement
The complement function returns the logic or bitwise complement of a value. It is used for bitmask
integer numbers and inverting bitsets.
false
print complement 3
-4
not
For a logic value not returns true if the value is false and false if the value is true. It does not
perform numerical bitwise operations.
false
Errors
Math errors are reported when an illegal operation is performed or when an overflow or underflow
occurs. The following are errors encountered in math operations.
An attempt was made to process a number too large for REBOL to handle.
1E+300
1E+400
An attempt was made to process a negative number with a math operator that accepts only positive
numbers.
log-10
-1
An attempt was made to process incompatible data types. The data type of the second argument in the
operation is returned as listed.
10:30 + 1.2.3
WWW.REBOL.COM
REBOL/Core Users Guide
Files
Updated: 17-Jul-2001
Table of Contents
1. Overview
2. Names and Paths
2.1. File Names
2.2. Path Strings
2.3. Case Sensitivity
2.4. File Name Functions
3. Reading Files
3.1. Reading Text Files
3.2. Reading Binary Files
3.3. Reading Over the Network
4. Writing Files
4.1. Writing Text Files
4.2. Writing Binary Files
4.3. Writing Files to a Network
5. Line Conversion
6. Blocks of Lines
7. File and Directory Information
7.1. Directory Check
7.2. File Existence
7.3. File Size
7.4. File Modification Date
7.5. Directory Information
8. Directories
8.1. Reading a Directory
8.2. Making a Directory
8.3. Renaming Directories and Files
8.4. Deleting Directories and Files
8.5. Current Directory
8.6. Changing the Current Directory
8.7. Listing the Current Directory
1. Overview
An important aspect of REBOL's power is its ability to manipulate files and directories. REBOL
provides a wide range of functions designed to allow operations ranging from simple file reads to
direct access to files and directories. For more information on direct access to files and
directories, see the Ports Chapter.
REBOL provides a standard, machine independent file and path naming convention.
In scripts, file names and paths are written with a percent sign (%) followed by a sequence of
characters:
%examples.r
%big-image.jpg
%graphics/amiga.jpg
%/c/plug-in/video.r
%//sound/goldfinger.mp3
The percent sign is necessary to prevent file names from being interpreted as words within the
language.
Although it is not a good practice, spaces can be included in file names by enclosing the file name
in double quotes (" "). The double quotes prevent the file name from being interpreted as multiple
words:
%"this file.txt"
%"cool movie clip.mpg"
The standard Internet convention of using a percent sign (%) and a hex code is also allowed for
character encoding. When this is done, quotes are not required. The above file names could also
be written as:
%this%20file.txt
%cool%20movie%20clip.mpg
Note that the standard file suffix for REBOL scripts is ".r". On systems where this convention
collides with another file type, a ".reb" suffix can be used instead.
File paths are written with a percent sign (%) followed by a sequence of directory names that are
each separated by a forward slash (/).
%dir/file.txt
%/file.txt
%dir/
%/dir/
%/dir/subdir/
%../dir/file.txt
The standard character for separating directories is the forward slash (/), not the backslash (\). If
backslashes are found they are converted to forward slashes:
probe %\some\cool\movie.mpg
%/some/cool/movie.mpg
REBOL provides a standard, operating system independent method for specifying directory paths.
Paths can be relative to the current directory or absolute from the top-level file structure of the
operating system.
File paths that do not begin with a forward slash (/) are relative paths.
%docs/intro.txt
%docs/new/notes.txt
%"new mail/inbox.mbx"
The standard convention of using double dots (..) to indicate a parent directory or a single dot (.)
to refer to the current directory is also supported. For example:
%.
%./
%./file.txt
%..
%../
%../script.r
%../../plans/schedule.r
File paths use the standard Internet convention of beginning absolute paths with a forward slash
(/). The forward slash indicates to start at the top level of the file system. (Generally, absolute
paths should be avoided to ensure machine-independent scripts.) The example:
%/home/file.txt
would refer to a disk volume or partition named home. Other examples are:
%/ram/temp/test.r
%/cd0/scripts/test/files.r
To refer to the C volume that is often used by Windows, the notation is:
%/C/docs/file.txt
%"/c/program files/qualcomm/eudora mail/out.mbx"
Notice in the above lines that the disk volume, C, is not written as:
%c:/docs/file.txt
The above example is not a machine independent format and causes an error.
If the first directory name is absent, and the path begins with double forward slashes (//), then the
file path is relative to the current volume:
%//docs/notes
In REBOL, file names are not case-sensitive by default. However, when new files are created by
the language, they keep the case they were typed in:
The above example creates the file name with the S and F in uppercase.
In addition, when file names are read from directories, the case is preserved:
For case-sensitive systems, such as UNIX, REBOL finds the closest case match to the specified
file. For example, if a script asks to read %test.r, but only finds %TEST.r, the %TEST.r file is read.
This behavior is necessary to allow machine-independent scripts.
Various functions are provided to help you create file names and paths. These are listed below in
File Name Functions.
to-file Converts strings and blocks into a file name or file path.
split-path Splits a path into its directory part and its file name.
clean-path Returns the absolute path that is equivalent to any given path
containing double dot (..) or dot (.).
what-dir Returns the absolute path to the current directory.
3. Reading Files
Files are read as a series of text characters or as binary bytes. The source of the file is either a
local file on your system or a file from the network.
The read function returns a string that holds the entire text of the file. In the above example, the
variable text refers to that string.
Within the string returned by read, line terminators are converted to newline characters,
regardless of what style of line termination is used on your operating system. This allows you to
write scripts that search for newline without concern for what particular character or characters
constitute a line termination.
A file can also be read as separate lines that are stored in a block:
To read a file a piece at a time, use the open function as described in the Ports Chapter.
To view the contents of a text file, you can read it using read and print it using print:
I wanted the gold, and I sought it,I scrabbled and mucked like
a slave.
The read/binary function returns a binary series that holds the entire contents of the file. In the
above example, the variable data refers to the binary series. No conversion of any type is done to
the file.
To read a binary file a piece at a time, use the open function as described in the Ports Chapter.
Files can be read from a network. For example, to view a text file from a network using the HTTP
protocol:
Hellotherenewuser!
In the write process the file will have its line termination converted to that which is used by your
operating system.
To read and save a binary file, such as an image, use the following line:
write %image.jpg
read/binary http:/www.rebol.com/image.jpg
Refer to the chapter on Network Protocols for more information and examples of accessing files
across networks.
4. Writing Files
You can write a file as a series of text characters or as binary bytes. The location of the file can be
either a local file on your system or a file on a network.
If a file contains newline characters, they will be converted to those used by your local file system.
This allows you to deal with files in a consistent manner, but write them out using the convention
that is standard to your file system.
For instance, the following line converts any text file from one line termination format (UNIX,
Macintosh, PC, Amiga) to that which is used by your local system:
The above line reads the entire file while converting its line termination to the REBOL standard,
then writes the file converting it to the local operating system format.
A file can also be written from separate lines that are stored in a block.
To write a file a piece at a time, use the open function as described in the Ports Chapter.
4.2. Writing Binary Files
The write/binary function creates the file if it does not exist or overwrites the file if it already
exists. No conversion of any type is done to the file.
To write a binary file a piece at a time, use the open function as described in the Ports Chapter.
Files can also be written to a network. For example, to write a text file to a network using FTP,
use:
The file can be read locally and written to the net with a line such as:
In the process, the file has its line termination converted to the standard CRLF format.
To write a binary file, such as an image, to the network, use the following lines of code:
write/binary ftp://ftp.domain.com/file.txt/image.jpg
read/binary %image.jpg
Refer to the chapter on Network Protocols for more information and examples of accessing files
from networks.
5. Line Conversion
When a file is read as text, all line terminators are converted to newline (line feed) characters.
Line feeds (used as line terminators on Amiga, Linux, and UNIX systems), carriage returns (used
as line terminators on Macintosh), and the CR/LF combination (PC and Internet) are all converted
to the equivalent newline characters.
Using a standard line terminator within scripts allows them to operate in a machine-independent
fashion. For example, to search for and count all newline characters within a text file:
When a file is written, the newline character is converted to the line termination style standard for
the operating system being used. For instance, the newline character is converted to a CRLF on
the PC, LF on UNIX or Amiga, or CR for a Macintosh. Network files are written with CRLF.
The following function converts any text file with any terminator style to that used by the local
operating system:
The file is read and all line terminators are converted, then the file is written and newline
characters are converted to the local operating system style.
Line conversion can be disabled by reading the file as binary. For instance, the following line:
6. Blocks of Lines
Text files can be easily accessed and managed as individual lines of text, rather than as a single
series of characters. For example, to read a file as a block of lines:
The above example returns a block containing a series of strings (one for each line) without line
terminators. Empty lines are represented by empty strings.
To print all of the lines of a file, use the following line of code:
To print all of the lines that contain the string gold, use the following line of code:
You can write the text file out as lines using the write function:
write/lines %output.txt [
"line one"
"line two"
"line three"
]
In fact, the functions read/lines and write/lines can be combined to process files one line at a
time. For example the following code removes all of the comments from a REBOL script:
The sample script in the above example is for demonstration purposes only. In addition to
removing comments, the sample script would also remove valid semicolons in quoted strings.
new
The /lines refinement can be used with the open function to read a line at a time from console
input. See the chapter on Ports for more information.
In addition /lines can be used with /append to append lines from a block to a file.
false
print dir? %.
true
To obtain the last modification date of a file, use the modified? function:
30-Jun-2000/14:41:55-7:00
Not all operating systems keep track of the creation date of a file, so to keep REBOL scripts
operating system independent only the last modification date is accessible.
The modified? function also works with some network protocols:
The info? function obtains all file directory information at the same time. The information is
returned as an object:
make object! [
size: 306
date: 30-Jun-2000/14:41:55-7:00
type: 'file
]
8. Directories
There are several easy-to-use functions for reading directories, managing subdirectories, making
new directories, renaming files, and deleting files. In addition, there are standard functions for
getting, changing, and listing the current directory. For more information on direct access to
directories, see the Ports Chapter.
8.1. Reading a Directory
Directories are read in the same manner as files. The read function returns a block of file names
rather than text or binary data.
To read all the file names from the current directory, use the following line of code:
read %.
The above example reads the entire directory and returns a block of file names.
To print the names of all files in a directory, use the following line of code:
Within the returned block, names of directories are indicated with a trailing forward slash. To print
each file name on a separate line, use:
CVS/
history.t
intro.t
overview.t
quick.t
Here is an easy way to print just the directories that were found:
CVS/
If you want to read a directory from the network, be sure to include a forward slash at the end of
the URL to indicate to the protocol that you are referring to a directory:
make-dir %new-dir
make-dir %local-dir/
make-dir %/work/docs/old-docs/
Internally, the make-dir function calls open with the /new refinement. The line:
also creates a new directory. The trailing slash is important in this example, indicating that a
directory is to be created rather than a file.
If you use the make-dir function to create a directory that already exists, an error will occur. The
error can be caught with the try function. The directory can be checked in advance with the
exists? function.
The old file name may include a complete path to the file, but the new file name must not include
a path. This is because the rename function is not intended to move files between directories
(various operating systems do not provide this function).
If the old file name is a directory (indicated by a trailing slash), the rename function renames the
directory:
If the file cannot be renamed, an error will occur. The error can be caught with the try function.
delete %source/docs/file.txt
A group of files can be deleted using a wildcard character and the /any refinement:
delete/any %file*
delete/any %secret.?
The asterisk (*) wildcard character matches all characters, and the question mark (?) wildcard
character matches a single character.
delete %dir/
delete %../docs/old/
If the file cannot be deleted, an error will occur. The error can be caught with the try function.
print what-dir
/work/REBOL/
The what-dir function refers to the current script's directory path, as found in system/script/path.
change-dir %new-path/to-dir/
list-dir
The number of columns used to show the directory is dependent on the console window size and
the maximum file name length.
Network Protocols
Updated: 12-Jul-2001
Table of Contents
1. Overview
2. REBOL Networking Basics
2.1. Modes of Operation
2.2. Specifying Network Resources
2.3. Schemes, Handlers, and Protocols
2.4. Monitoring Handlers
3. Initial Setup
3.1. Basic Network Settings
3.2. Proxy Settings
3.3. Other Settings
3.4. Access to Settings
4. DNS - Domain Name Service
5. Whois Protocol
6. Finger Protocol
7. Daytime - Network Time Protocol
8. HTTP - Hyper Text Transfer Protocol
8.1. Reading a Web Page
8.2. Scripts on Web Sites
8.3. Loading Markup Pages
8.4. Other Functions
8.5. Acting Like a Browser
8.6. Posting CGI Requests
9. SMTP - Simple Mail Transport Protocol
9.1. Sending Email
9.2. Multiple Recipients
9.3. Bulk Mail
9.4. Subject Line and Headers
9.5. Debug Your Scripts
10. POP - Post Office Protocol
10.1. Reading Email
10.2. Removing Email
10.3. Handling Email Headers
11. FTP - File Transfer Protocol
11.1. Using FTP
11.2. FTP URLs
11.3. Transferring Text Files
11.4. Transferring Binary Files
11.5. Appending to Files
11.6. Reading Directories
11.7. File Information
11.8. Making Directories
11.9. Deleting Files
11.10. Renaming Files
11.11. About Passwords
11.12. Transferring Large Files
12. NNTP - Network News Transfer Protocol
12.1. Reading the Newsgroup List
12.2. Reading All Messages
12.3. Reading Single Messages
12.4. Handling News Headers
12.5. Sending a News Message
13. CGI - Common Gateway Interface
13.1. CGI Server Setup
13.2. CGI Scripts
13.3. Generating HTML Content
13.4. CGI Environment
13.5. CGI Requests
13.6. Processing HTML Forms
14. TCP - Transmission Control Protocol
14.1. Creating Clients
14.2. Creating Servers
14.3. A Tiny Server
14.4. Testing TCP Code
15. UDP - User Datagram Protocol
1. Overview
REBOL includes several of the primary Internet service protocols built-in. These protocols are easy to use within your
scripts; they require no extra libraries or include files, and many useful operations can be done with only a single line of
source code.
DNS Domain Name Service: translates computer names into addresses and addresses into
names.
Finger Obtains information about a user from their profile.
In addition, you can create handlers for other Internet protocols or make your own custom protocols.
There are two basic modes of network operation: atomic and port-based.
Atomic network operations are those that are accomplished in a single function. For instance, you can read an entire Web
page with a single call to the read function. There is no need to separately open a connection or set up the read. All of
that is done automatically as part of the read. For example, you can type:
The host is found and opened, its Web page transferred, and the connection closed.
The port-based mode of operation is one that uses a more traditional programming approach. It involves opening a port
and performing various series operations on the port. For instance, if you want to read your email from a POP server one
message at a time, you would use this method. Here is an example that reads and displays all of your email:
The atomic method of operation is easier, but it is also more limited. The port-based method allows more types of
operations, but also requires a greater understanding of networking.
REBOL provides two approaches for specifying network resources: URLs and port specifications.
Uniform Resource Locators (URL) are used on the Internet to identify a network resource, such as a Web page, FTP site,
email address, file, or other resource or service. URLs are integral to the operation of REBOL, and they can be expressed
directly in the language.
scheme:specification
The scheme is often the name of a protocol, such as HTTP, FTP, SMTP, and POP; however, that is not a requirement. A
scheme can be any name that identifies the method used to access a resource.
The format of a scheme's specification depends on the scheme; however, most schemes share a common format for
identifying network hosts, user names, passwords, port numbers, and file paths. Here are a few commonly used formats:
scheme://host
scheme://host:port
scheme://user@host
scheme://user:pass@host
scheme://user:pass@host:port
scheme://host/path
scheme://host:port/path
scheme://user@host/path
scheme://user:pass@host/path
scheme://user:pass@host:port/path
Network Resource Specification lists the fields used in the above formats.
scheme The name used to identify the type of resource, often the same as the protocol. For
example, HTTP, FTP, and POP.
host The network name or address for a machine. For example, www.rebol.com, cnn.com,
accounting.
port Port number on the host machine for the scheme being used. Normally there is a default
for this, so it is not required most of the time. Examples: 21, 23, 80, 8000.
user A user name to access the resource.
path A file path or some other method for referencing the resource. This is scheme dependent.
Some schemes include patterns and script arguments (such as CGI).
Another way to identify a resource is with a REBOL port specification. In fact, when a URL is used, it is automatically
converted into a port specification. A port specification can accept many more arguments than a URL, but it requires
multiple lines to express.
A port specification is written as an object block definition that provides each of the parameters necessary to access the
network resource. For instance, the URL to access a Web site is:
read http://www.rebol.com/developer.html
read [
scheme: 'HTTP
host: "www.rebol.com"
target: %/developer.html
]
read ftp://bill:vbs@ftp.example.com:8000/file.txt
read [
scheme: 'FTP
host: "ftp.example.com"
port-id: 8000
target: %/file.txt
user: "bill"
pass: "vbs"
]
In addition, there are many other port fields that can be specified, such as timeout, type of access, and security.
REBOL networking operates by using schemes to identify handlers that communicate with protocols.
In REBOL a scheme is used to identify the method of accessing a resource. That method uses a code object that is called
a handler. Each of the URL schemes that are supported by REBOL (such as HTTP, FTP) has a handler. The list of
schemes can be obtained with:
In addition, there are lower level scheme names that are not shown here. For instance, the TCP and UDP schemes are
used for direct, lower level communication.
New schemes can be added to this list. For instance, you can define your own scheme, called FTP2, that provides special
features for FTP access, such as automatically supplying your username and password so it does not need to be
included in every FTP URL.
Most handlers are used to provide an interface to a network protocol. A protocol is used to communicate between various
devices, including clients and servers.
Although each protocol is quite different in how it communicates, it does have some things in common with other
protocols. For instance, most protocols require a network connection to be opened, read, written, and closed. These
common operations are performed by a default handler in REBOL. This handler makes protocols like finger, whois, and
daytime almost trivial to implement.
Scheme handlers are written as objects. The default handler serves as the root object for all the other handlers. When a
handler requires a particular field, such as a timeout value to use for reading data, if the value is not defined in the specific
handler, it will be provided by the default handler. Hence, handlers overlay one another with their fields and value. You
can also create handlers that use other handlers for default values. For instance, you can create an FTP2 handler that
looks for missing fields first in the FTP handler, then in the default handler.
When a port is used to access network resources, it is linked to a specific handler. The handler and the port together form
the unit that is used to provide the data, code, and state information to process all protocols.
The source code to handlers can be obtained from the system/scheme object. This can be useful if you want to modify
the behavior of a handler or build your own handler. For instance, to view the code for the whois handler, type:
Note that what you are seeing is a composite of the default handler with the whois handler. The actual source code that is
used to create the whois handler is only a few lines:
make Root-Protocol [
open-check: [[any [port/user ""]] none]
net-utils/net-install Whois make self [] 43
]
2.4. Monitoring Handlers
For debugging purposes, you can monitor the actions of any handler. Each handler has its own debugging output to
indicate what operations are being performed. To enable network debugging, turn network tracing on with the line:
trace/net on
trace/net off
Here is an example:
read pop://carl:poof@zen.example.com
3. Initial Setup
REBOL networking is built-in. To create scripts that use the network protocols you do not need any special include files or
libraries. The only requirement is that you provide the basic information necessary to enable protocols to connect to
servers or through firewalls and proxies. For instance, to send an email, the SMTP protocol needs an SMTP server name
and a reply email address.
When you run REBOL the first time, you re prompted for the necessary network settings, which is stored in the user.r file.
REBOL uses this file to load the required network settings each time it is started. If a user.r is not created and REBOL
cannot find an existing user.r file in its paths, no settings are loaded. See the chapter on Operation for more information.
To change the network settings, type set-user at the prompt. This runs the same network configuration script that ran
when REBOL first started. This script is loaded from the rebol.r file. If that file cannot be found, or if you want to edit the
setting directly, you can use a text editor on the user.r file.
Within the user.r file the network settings are found in a block that follows the set-net function. At a minimum the block
should contain two items:
Your email address for use in the from and reply fields of email and for anonymous FTP login
Your default server; this is also your primary email server
In addition, you can specify a few other items:
You can also add lines after the set-net function to configure other protocol values. For instance you can set the timeout
values for protocols, set the FTP passive mode, set the HTTP user-agent identifier, set up separate proxies for different
protocols, and more.
The first field specifies your email from address, and the second field indicates your default server (notice that it does not
need quotes here). For most networks, this is enough and no other settings are necessary (unless you require a proxy).
Also your default server is used whenever a specific server is not provided.
In addition, if you use a POP server (for incoming email) that is different from your SMTP server (for outgoing email), you
can specify that as well:
set-net [
user@domain.dom
mail.server.dom
pop.server.dom
]
However, if your SMTP and POP servers are the same, then this is not necessary.
If you use a proxy or firewall, you can provide the set-net function with your proxy settings. This can include the proxy
server name or address, a proxy port number to access the server, and an optional proxy type. For example:
set-net [
email@addr
mail.example.com
pop.example.com
proxy.example.com
1080
socks
]
This example would use a proxy called proxy.example.com on its TCP port 1080 with the socks proxy method. To use a
socks4 proxy server, use the word socks4 rather than socks. To use the generic CERN server, use the word generic.
You can also set the proxy to be different machines for different schemes (protocols). Each protocol has its own proxy
object where you can set the proxy values for just that scheme. Here is an example of setting a proxy for FTP:
system/schemes/ftp/proxy/host: "proxy2.example.com"
system/schemes/ftp/proxy/port-id: 1080
system/schemes/ftp/proxy/type: 'socks
In this case, only FTP uses a special proxy server. Notice that the machine name must be a string and the proxy type
must be a literal word.
Here are two more examples. The first example sets the proxy for HTTP to be the generic (CERN) proxy method:
system/schemes/http/proxy/host: "wp.example.com"
system/schemes/http/proxy/port-id: 8080
system/schemes/http/proxy/type: 'generic
In the above example, all HTTP requests go through a generic proxy on wp.example.com using TCP port 8080.
If you want to disable the proxy settings for a particular scheme, you can set the proxy fields to false.
system/schemes/smtp/proxy/host: false
system/schemes/smtp/proxy/port-id: false
system/schemes/smtp/proxy/type: false
In the above example, all outgoing email does not go through a proxy. The false value prevents even the default proxy
from being used. If you set these fields to none, then the default proxy is used if it is configured.
If you want to bypass the proxy settings for particular machines, such as those on your local network, you can provide a
bypass list. Here is a bypass list for the default proxy:
system/schemes/default/proxy/bypass:
["host.example.net" "*.example.com"]
Note that the asterisk (*) and question mark (?) characters can be used for pattern matching. The asterisk (*) as used in
the example above bypasses any machine that ends with example.com.
system/schemes/http/proxy/bypass:
["host.example.net" "*.example.com"]
In addition to proxy settings, you can set network timeout values for all of the schemes (in the default) or for specific
schemes. For instance, to increase the timeout for all schemes, you can write:
system/schemes/default/timeout: 0:05
If you want to increase the timeout just for SMTP, you would write:
system/schemes/smtp/timeout: 0:10
Some schemes have custom fields. For instance, the FTP scheme allows you to set passive mode for all transfers:
system/schemes/ftp/passive: on
FTP passive mode is useful because FTP servers that are set to passive mode do not attempt to connect back through
your firewall.
When making HTTP accesses to Web sites, you may want to use a different user-agent field in the HTTP request to get
better results on a few sites that detect the browser type:
system/schemes/http/user-agent: "Mozilla/4.0"
Each time REBOL is started, it reads the user.r file to find its network settings. These settings are made with the set-net
function. Scripts have access to these settings through the system/schemes object.
Below is a function that returns a block containing the network settings in the same order as set-net accepts them:
probe get-net
The DNS protocol can be used in three ways: you can lookup the primary IP address of a machine name, you can lookup
the domain name for an IP address, and you can find the name and IP address of your local system.
To lookup the primary IP address of a specific machine within a specific domain, type:
207.69.132.8
You can also obtain the domain name that is associated with a particular IP address:
rebol.com
Note that it is not unusual for this reverse DNS lookup to return a none. There are machines that do not have host names.
none
To find your system's host name, read an empty DNS URL of the form:
crackerjack
The data returned here depends on the type of machine. It may be the unqualified host name, as shown above, but it can
also be the fully-qualified host name, crackerjack.example.com. This depends on the operating system and the network
configuration in the operating system.
Here's an example that looks up and prints the IP addresses for a number of Web sites:
domains: [
www.rebol.com
www.rebol.org
www.mochinet.com
www.sirius.com
]
5. Whois Protocol
The whois protocol retrieves information about domain names from a central registry. The whois service is provided by
the organizations that run the Internet. Whois is often used to retrieve registration information about an Internet domain or
server. It can tell you who owns the domain, how their technical contact can be reached, along with other information.
To obtain information, use the read function with a whois URL. This URL should contain the domain name and a whois
server name separated by an at sign (@). For example to obtain information about example.com from the Internic
registry:
The above code is only an example. The details of the information being returned and the servers that support whois
change over time.
If instead of a domain name you provide a word, all entries that match that word are returned:
The whois protocol does not accept URLs, such as www.example.com, unless the URL is part of the registrant's company
name.
6. Finger Protocol
The finger protocol retrieves user-specific information stored in the user log file.
To request user information from a server it must be running the finger protocol. The information is requested by reading
a finger URL that contains a username and a domain name in an email style format:
The above example retrieves information about the user at username@example.com. The information returned depends
on the information provided by the user and the settings of the finger server. Also, the details of the information being
returned are up to each server; the examples below only describe typical servers. Many servers can have non-standard
behaviors on their finger ports.
Login: username
Name: Firstname Lastname
Directory: /home/user
Shell: /usr/local/bin/tcsh
Office: City, State +1 555 555 5555
Last login Wed Jul 28 01:10 (PDT) on ttyp0 from some.example.com
No Mail.
No Plan.
Notice that finger reports when the user last logged in from a machine, and whether the user has mail waiting. If the user
reads email from this account, finger sometimes reports when mail was received and when the user last retrieved email:
The finger server can also report the contents of a plan file and a project file if they exist. Users can include any
information they want in a plan or project file.
It is also possible to retrieve information about users using their real first name or their last name. Some finger servers
require that you capitalize the names exactly as they appear in the login file or in the file used by the online finger server,
to retrieve user information. Other finger servers are more liberal about capitalization. A finger server will respond to real
name queries by returning all listings that match the query criteria. For instance, if there are several users on a host that
have the first name zaphod, then entering the query
will retrieve all such users whose first or last name is Zaphod.
Some finger servers return a listing of users when the user name is omitted. For example,
retrieves a list of all users who are logged onto the machine, if the finger service installed on the hosting machine allows
it.
Some host machines limit finger services for security reasons. They may require a valid username and only return
information regarding that user. If you finger such a server without providing user information, the server will report that it
requires specific user information.
If a system does not support the finger protocol, REBOL reports an access error:
If the server you choose does not support daytime, REBOL returns an error:
To retrieve a Web page, the browser sends a request to a Web server using HTTP. On receiving the request, the server
interprets it, sometimes using a CGI script (see CGI - Common Gateway Interface), and sends back data. This data can
be just about anything, including HTML, text, images, programs, and sound.
To read a Web page, use the read function with an HTTP URL. For example:
This returns the Web page for www.rebol.com. Note that a string that contains the HTML code for the page is returned by
the read. No graphics or other information are fetched. To do so you would need to provide additional reads. The page
can be displayed as HTML code using print, it can be written to a file with write, or it can be sent as email using send.
print page
The page can be processed in a variety of ways by using a variety of REBOL functions, such as parse, find, and load.
For instance, to search a Web page for all occurrences of the word REBOL, you can write:
A Web server can provide more than just HTML scripts. Web servers are quite useful for supplying REBOL scripts as
well.
You can load REBOL scripts directly from a Web server with load:
You can also evaluate scripts directly from a server with do:
data: do http://www.rebol.com/code.r
Warning
Do this with care. Evaluating arbitrary scripts on open Internet servers is asking for trouble.
Evaluate a script only if you completely trust its source, have fully inspected its source, or have
kept your REBOL security settings on maximum.
In addition, Web pages that contain HTML can contain embedded REBOL scripts, and they can be run with:
data: do http://www.rebol.com/example.html
To determine if a script exists on a page before evaluating it, use the script? function.
The script? function reads the page from the Web site and returns the page at its REBOL header position.
HTML and XML pages can be quickly converted to a REBOL block with the load/markup function. This function returns a
block that consists of all the tags and strings found within the page. All spacing and line breaks are left intact.
To filter out all of the tags for a Web page and just print its text, type:
print text
You could then search this text for string patterns. It will contain all of the spaces and line breaks of the original HTML file.
Here's another example that checks all links found on a Web page to make sure that the pages they reference exist:
REBOL []
page: http://www.rebol.com/developer.html
set [path target] split-path page
system/options/quiet: true ; turn off connection msgs
tag-text: load/markup page
links: make block! 100
print links
To check if a Web page exists, use the exists? function, which returns true if the page exists.
if exists? http://www.rebol.com [
print "page still there"
]
Note: It is usually faster in many cases to just read the page rather than checking first to see if it exists. Otherwise the
script must contact the server twice, and that can be time consuming.
To request the date on which a Web page was last modified, use the modified? function:
However, note that not all Web servers provide modification date information. Dynamically generated Web pages typically
do not return a modification date.
Another way to determine if a Web page has changed is poll it every so often and check it. A handy way to verify that a
Web page has changed is by using the checksum function. If the previously calculated checksum of a Web page differs
from its current value, then the Web page has been modified since it was last checked. Here is an example that uses this
technique. It checks a page every eight hours.
forever [
page: read http://www.rebol.com
page-sum: checksum page
if any [
not exists? %page-sum
page-sum <> (load %page-sum)
][
print ["Page changed" now]
save %page-sum page-sum
send luke@rebol.com page
]
wait 8:00
]
Normally, REBOL identifies itself to a server when it reads from a Web site. However, some servers are programmed to
respond to particular browsers only. If a request to a server does not produce the correct Web page, you can change the
request to make it look like it came from some other type of Web browser. Pretending to be a Web browser is done by
many programs to get Web sites to respond correctly. However, this practice does end up defeating the purpose behind
the browser identification.
To change HTTP requests to look as though they are being sent by Netscape 4.0, you can modify the user-agent within
the HTTP handler:
system/options/http/user-agent: "Mozilla/4.0"
HTTP CGI requests can be posted in two ways. You can include the CGI request data in the URL or you can provide the
request data through an HTTP post operation.
The URL CGI request uses a normal URL. The example below sends the CGI script test.r the data value of 10.
read http://www.example.com/cgi-bin/test.r?data=10
The post CGI request requires that you supply the CGI data as part of a custom refinement to the read function. The
example below shows how data is posted to CGI:
read/custom http://www.example.com/cgi-bin/test.r [
post "data: 10"
]
In this example, the /custom refinement is used to provide additional information to the read. The second argument is a
block that begins with the word post and is followed by the string to send.
The post method is useful for easily sending REBOL code and data to a web server that runs CGI. The following example
illustrates this:
The mold function will produce the proper REBOL string to be sent to the server.
9. SMTP - Simple Mail Transport Protocol
The Simple Mail Transport Protocol (SMTP) controls the transfer of email messages on the Internet. SMTP defines the
interaction between Internet hosts that participate in forwarding email from a sender to its destination.
Email is sent through SMTP by using the send function. This function can send an email message to one or more email
addresses.
For send to operate correctly, your networking must be set up. The send function requires that you specify your email
From address and your default email server. See Initial Setup above.
The send function takes two arguments: an email address and a message. For example:
The first argument must be an email or block data type. The second argument can be any data type.
Each of these simple email messages can be interpreted on the receiver's side (with REBOL) or viewed with a normal
email program.
You can send an entire file by reading the file and passing it as the second argument to the send function:
When the message is received, the file can be extracted by using the do function.
In this case, each message is individually addressed with only the recipient's email name appearing in the To field (similar
to BCC addressing).
The block of email addresses can be any size or even a file that you load. Just be sure that they are valid addresses, not
strings. Strings are ignored.
friends: [
bob@cnn.dom
betty@cnet.dom
kirby@hooya.dom
belle@apple.dom
...
]
If you are sending email to a large group, you can reduce the load on your server by delivering everyone in the group a
single message. This is the purpose of the /only refinement. It uses a feature of SMTP to send only one message to
multiple email addresses. Using the friends list from the previous example:
The messages are not individually addressed. You may have seen this mode in some of the bulk email that you receive.
When you receive bulk email, your address does not appear in the To field.
The bulk email mode of SMTP should be used for email lists and not for sending spam. Spam email is not proper network
etiquette, it is illegal in some countries and states, and spam will get you banned from your ISP and from other sites.
By default the send function uses the first line of a message as the subject line. To provide your own subject line, you
need to supply an email header to the send function. In addition to a subject line, you can provide an organization, date,
CC, and even your own custom fields.
To include a header, use the /header refinement of the send function and include a header object. The header object
must be made from the system/standard/email object. For example:
Notice that the standard fields, such as the From address, are not required and are supplied automatically by the send
function.
The email above is sent using the custom header for each message.
9.5. Debug Your Scripts
When testing email scripts, it is advised that you send email to yourself first, before sending it to others. Examine your test
email carefully to make sure that it is what you want. It is common to have errors such as sending a file name rather than
the file contents. For instance, you might write:
This sends the name of the file, not the file itself.
You can read all of your email in a single line without removing it from the email server: This is done by reading from a
POP URL in which you provided your username, password, and email host.
The messages are returned as a block of strings which you can handle one message at a time using code such as:
To read individual email messages from the server, you need to open a port connection to the server and handle each
message one at a time. To open the POP port:
In the example, mailbox can be accessed as a series. It responds to many of the standard series functions, such as
length?, first, second, third, pick, next, back, head, tail, head?, tail?, remove, and clear.
To determine the number of mail messages residing on the server, use the length? function.
37
In addition, you can find out the total size of all messages and the individual sizes of messages with:
print mailbox/locals/total-size
print mailbox/locals/sizes
To display the first, second, and last messages, you can write:
print first mailbox
You can fetch and display each message from the oldest to the newest using a loop that is identical to that used for other
types of series:
You can also read your email from newest to oldest with a loop such as:
When you are done, be sure to close the mailbox. This can be done with a line such as:
close mailbox
As with series, the remove function can be used to delete a single message, and the clear function can be used to delete
all of the messages from the current position to the end of the mailbox.
For example, to read a message, save it to a file, and remove it from the server:
The message is removed from the server when the close is done.
To remove the 22nd email message from the server, you can write:
user:pass@mail.example.com
remove at mailbox 22
close mailbox
You can remove a number of messages by using the /part refinement with the remove function:
remove/part mailbox 5
To remove all of the messages in your mailbox, use the clear function:
mailbox: open pop://user:pass@example.com
clear mailbox
close mailbox
The clear function can also be used at different positions within the mailbox to remove messages to the end of the
mailbox.
Email messages always include a header. The header holds information such as the sender, recipient, subject, date, and
other fields.
In REBOL email headers are handled as objects that contain all of the necessary fields. To convert email message to a
header object you can use the import-email function. For example:
You can easily write a filter that scans your email for messages that begin with a particular subject line:
close mailbox
Here is another example that informs you when email is received from a group of friends:
This spam filter removes all messages from the server that do not contain your email name anywhere within the
message:
close mailbox
Here is a simple email list server that receives messages and sends them to a group. The server only accepts email from
people in the group.
close mailbox
In REBOL FTP file operations are handled in much the same way as local file operations. Functions such as read, write,
load, save, do, open, close, exists?, size?, modified?, and others are used with FTP. REBOL distinguishes between
local files and files accessible by FTP through the use of an FTP URL.
Access to FTP servers can be open or closed. Open access allows anyone to login to the site and download files. This is
called anonymous access and it is used frequently for public file archives. Closed access requires that you provide a
username and password to download and upload files. This is the mode of operation for uploading Web pages to a Web
site.
Although FTP does not require your REBOL networking to be configured, if you wish to use anonymous access, an email
address is required. This address is found in the system/user/email object. Normally, when you boot REBOL, this field is
set from your user.r file. See Initial Setup for more detail.
If you are using FTP through a proxy server or firewall, FTP may need to operate in passive mode. Passive mode does
not require reverse connections from the FTP server to the client for data transfers. This mode only makes outgoing
connections from your machine and allows a greater level of security. To enable passive mode you need to set a flag in
the FTP protocol handler:
system/schemes/ftp/passive: true
If you do not know if it is necessary, try FTP first without it. If that does not work, try setting the passive flag.
11.2. FTP URLs
ftp://user:pass@host/directory/file
For anonymous access the username and password can be left out:
ftp://host/directory/file
Most of the examples in this section use this form for simplicity; however, they also work with a username and password.
To access a remote directory, end the URL with a slash, such as:
ftp://user:pass@host/directory/
ftp://host/directory/
ftp://host/
It is convenient to put the URL in a variable and use paths to provide the file names. This allows you to refer to the URL
with just a word. For example:
site: ftp://ftp.rebol.com/pub/
read site/readme.txt
FTP distinguishes between text files and binary files. When transferring text files, FTP converts the line break characters.
This is not desirable for binary files.
To read a text file, supply the read function with an FTP URL:
This puts the contents of the file into a string. To write the file locally, use this line:
Many of the refinements of read can also be used. For instance, you can use read/lines with:
This example returns a block of lines for the file. See the Files chapter for more information about the refinements to the
read function.
As with normal text file transfers, all line termination will be properly converted during FTP transfers.
site: ftp://wwwuser:secret@www.site.dom/pages
This should not be used for transferring graphics or sound files, as they are binary. Use the technique shown in
Transferring Binary Files.
In addition to the read and write functions, you can use the load, save, and do functions with FTP.
do ftp://ftp.site.com/scripts/test.r
To avoid the line termination conversion when transferring binary files (images, archives, executable files), use the
/binary refinement. For instance, to read a binary file from an FTP server:
site: ftp://user:pass@ftp.site.com/www/graphics
FTP also allows you to append text and data to an existing file. To do so, use the write/append refinement as described
in the Files chapter.
write/append ftp://ftp.site.com/pub/log.txt reform
["Log entry date:" now newline]
write/binary/append ftp://ftp.site.com/pub/log.txt
read/binary %datafile
To read the file names of an FTP directory, follow the directory name with a forward slash:
The ending forward slash (/) indicates that this is a directory access not a file access. The forward slash is not always
required, but it is recommended when you know you are accessing a directory.
The block of files that is returned includes all of the files in the directory. Within that block, directory names are indicated
with a forward slash following their names. For example:
readme.txt
rebol.r
rebol.exe
library/docs/
You can also use the dir? function on a file to determine if it is a directory.
The same functions that provide information about files also provide information about FTP files. This includes the
modified?, size?, exists?, dir?, and info? functions.
if exists? ftp://ftp.site.com/pub/log.txt [
print "Log file is there"
]
This works for directories too, but include the forward slash at the end of the directory name:
if exists? ftp://ftp.site.com/pub/rebol/ [
print read ftp://ftp.site.com/pub/rebol/
]
if dir? ftp://ftp.site.com/pub/text [
print "It's a directory"
]
You can obtain all this information in a single access by using the info? function:
probe file-info
print file-info/size
forall files [
file: first files
info: info? file
print [file info/date info/size info/type]
]
make-dir ftp://user:pass@ftp.site.com/newdir/
With appropriate permission settings, files can be deleted from a remote FTP server by using the delete function:
delete ftp://user:pass@ftp.site.com/upload.txt
delete ftp://user:pass@ftp.site.com/newdir/
To rename a directory on an FTP site be sure to follow the directory name with a slash:
The above examples include the password within their URLs, but if you plan on sharing your script, you probably don't
want that information to be known. Here's a simple way to prompt for a password and build the correct URL:
Or, you can ask for both the username and password:
You can also open FTP connections by using a port specification rather than a URL. This allows you to use any
password, even ones containing special characters that are not easily written in URLs. An example of a port specification
to open an FTP connection is:
ftp-port: open [
scheme: `ftp
host: "ftp.site.com"
user: ask "Username? "
pass: ask "Password? "
]
Transferring large files requires special considerations. You may want to transfer the file in chunks to reduce the memory
required by your computer and to provide user feedback while the transfer is happening.
Be sure to use the /direct refinement, otherwise the entire file will be buffered internally by REBOL. The read-io and
write-io functions allow reuse of the buffer memory that has already allocated. Other functions such as copy would
allocate additional memory.
If the transfer fails, you can restart FTP from where it left off. To do so, examine the output file or the size variable to
determine where to restart the transfer. Open the file again with a custom refinement that specifies restart and the
location from which to start the read. Here is an example of the open function to use when the total variable indicates the
length already read:
inp: open/binary/direct/custom
ftp://ftp.site.com/big-file.bmp
reduce ['restart total]
You should note that restart only works for binary transfers. It cannot be used with text transfers because the line
terminator conversion that takes place will cause incorrect offsets.
The built-in support for NNTP that provides very limited functionality and access. This is the NTTP scheme.
An extended level of functionality that is provided by the news scheme that is implemented in the file distributed as nntp.r.
NNTP consists of two components: a list of newsgroups supported by a specific newsgroup server (newsgroups are
typically selected by an Internet service provider); and, a database of messages that are currently available for any
particular newsgroup.
To retrieve the list of all newsgroups from a specific news server, use the read function with an NNTP URL such as:
This may take a while, depending on your connection; there are thousands of newsgroups.
If you are using a fast connection, you can read all of the pending messages for a newsgroup with:
messages: read nntp://news.example.com/alt.test
However, caution is advised. Some newsgroups can have thousands of messages. It can take a long time to download all
the messages, and you may run out of memory to hold them.
To read single messages, open NNTP as a port and use series functions to access messages. This is similar to how you
read email from a POP port. For example:
You can use the length? function to determine the number of messages that are available in the newsgroup:
To create a simple loop that scans all messages for a keyword, use:
forall group [
if find msg: first first group "REBOL" [
print msg
]
]
Remember that when the loop returns, the group series is positioned to the tail. If you need to return to the head of the
group:
close group
News messages always include a header. The header holds information such as the sender, summary, keywords,
subject, date, and other fields.
Headers are handled as objects. To convert a news message to a news header object you can use the import-email
function. For example:
Different newsgroups and newsgroup clients use different fields in their header. To view the fields available for a specific
message display the first item of the header object:
Before you can send a news message, you need to create a header for it. Here is a generic header that can be used for
news:
Before you can send it, you need to create a unique global identification number for it. Here is a function that does that:
make-id: does [
rejoin [
"<"
system/user/email/user
"."
checksum form now
"."
random 999999
"@"
read dns://
">"
]
]
<carl.4959961.534798@fred.example.com>
Now you can combine the header with the message. They must be separated by at least one blank line. The content of
the message is read from a file.
Setting up CGI access is different for every Web server. See the instructions provided with your server.
Typically a server has an option for enabling CGI operation. You need to enable this option and provide a path to the
directory where your CGI scripts reside. A common directory for CGI scripts is in cgi-bin.
On Apache servers, the ExecCGI option enables CGI scripts, and you can provide a directory (cgi-bin ) for your scripts.
This is normally set up by default installation of Apache.
To configure CGI for Microsoft IIS, go to the properties for cgi-bin and click on the configuration button. On the
configuration panel click add and enter the path to your rebol.exe file. The format for this is:
C:\rebol\rebol.exe -cs %s %s
The two %s symbols are required for correctly passing the script and command line arguments to REBOL. Add the
extension for REBOL files (.r ) and set the last field to PUT, DELETE. The script engine does not need to be selected.
The -cs option that is provided to REBOL enables CGI operation and allows the script to access all files. (!!See notes
below on how scripts can limit file access to selected directories).
With Web servers other than those described above, the server requires configuration to execute the REBOL executable
for .r extension files and run REBOL with the required option -cs.
Before a script can be executed on most CGI servers, it needs to have the correct file permissions. On UNIX-type
systems or those that use the Apache server you need to change the permissions to enable the script to be readable and
executable by all users. This can be done with the chmod function. If you are new to this concept, you should read your
operating system manual or talk with your system administrator before changing file permissions.
For Apache and various other Web servers to run REBOL scripts, you need to provide the correct header at the top of
each script file. The header specifies the path to the REBOL executable file and the -cs option. This can be followed by
the normal REBOL script header. Here is a simple CGI script that prints the string, hello!.
#!/path/to/rebol -cs
print "Hello!"
There are many things that prevent a CGI script from running correctly. Get this simple script working first before you try
more complex scripts. If your script does not work, here are a few items to check:
You have CGI enabled on your Web server.
The first line begins with a #! and the correct path to REBOL.
The -cs option is supplied to REBOL.
The script begins with "Content-Type:" being printed. (!!see below)
The script is in the correct directory. (normally the cgi-bin directory)
The script has the correct file permissions (readable and executable by all).
The script contains the correct line break characters. Some servers do not run scripts that contain the CR
character for line breaks. You may need to convert the file. (Use REBOL to do this in one line: write file, read file).
The script does not contain errors. Test it without CGI to make sure that the script loads (does not have syntax
errors) and functions properly. Provide some sample data and test it.
All files that are accessed by the script have the correct file permissions.
Often one or more of the above items is wrong and prevent your script from running. You may see an error when viewing
the Web page. If it says "Server Error" or "CGI Error" then it is typically something to do with the permissions or setup of
the script. If it shows a REBOL error message, then the script is running, but you have an error within the script.
In the example script shown above, the Content-Type line is critical. It is part of the HTTP header that is returned to the
browser, and it tells the browser the type of content being delivered. This is followed by a blank line to separate it from the
actual content.
Many different types of content can be delivered. The previous example was plain text, but you can also deliver HTML as
is shown in the next example. (See your Web server manual for more information about content types.)
The content type and blank line can be combined into a single line. The caret forward slash (^/) symbol provides an
additional line break to separate it from the content.
It is a good practice to always print this line immediately from your script. This allows error messages to be seen by the
browser if your script encounters an error.
#!/path/to/rebol -cs
There are as many ways to create HTML content as there are ways to create strings. This page creates a page that
displays a page hit counter:
#!/path/to/rebol -cs
print [
{<HTML><BODY><H2>Web Counter Page</H2>
You are visitor} count {to this page!<P>
</BODY></HTML>}
]
The script in the example above loads and saves to a counter text file. For this file to be accessible, it will require the
appropriate permissions be set to allow access by all users.
When a CGI script is run the server provides information to REBOL about the CGI request and its arguments. All of this
information is provided as an object within the system/options object. To view the fields of the object, type:
probe system/options/cgi
make object! [
server-software: none
server-name: none
gateway-interface: none
server-protocol: none
server-port: none
request-method: none
path-info: none
path-translated: none
script-name: none
query-string: none
remote-host: none
remote-addr: none
auth-type: none
remote-user: none
remote-ident: none
Content-Type: none
content-length: none
other-headers: []
]
Of course, your script will ignore most of this information, but some of it could be of use. For instance, you may want to
create a log file that records the network address of the system that made the request, or check the type of browser being
used.
#!/path/to/rebol -cs
probe system/options/cgi
If you want to use this information in a log, you can write it to a file. For example, to log the addresses of visitors to your
CGI page you could write:
write/append/lines %cgi.log
system/options/cgi/remote-addr
The /append and /lines refinements causes the write to be at the tail of the file and include a line-break. Here's another
approach that puts multiple items on the same line:
There are two methods for CGI to provide request data to your scripts: GET and POST.
The GET method encodes CGI data into the URL. This is used to provide information to the server. You may have
noticed before that some URLs look like this:
http://www.example.com/cgi-bin/test.r?&data=test
The string that follows the question mark (?) provides the arguments to CGI. At times they can be quite long. This string is
provided to your script when it is run. It can be obtained from the cgi/query-string field. For instance, to print the string
from a script:
print system/options/cgi/query-string
The data within the string can include whatever data you require. However, because the string is part of a URL, data must
be encoded. There are restrictions on the characters that are allowed.
In addition, when the data is created by HTML forms, it is encoded in a standard way. This data can be decoded and
placed within an object with the code:
The decode-cgi-query function returns a block that contains variable names and their values. See the HTML form
example in the next section.
The POST method provides the CGI data as a string. The data does not need to be encoded. It can be in any format you
desire and can even be binary. Post data is read from the standard input device. You will need to read it from the input
with a line such as:
This would read up to the first 2000 bytes of POST data and put it in a string.
A good format for POST data is to use a REBOL dialect and create a simple parser. The POST data can be loaded and
parsed as a block. See the Parsing chapter.
It is not a good idea to pass REBOL blocks to be directly evaluated because this can present a
security risk. For instance, someone could POST a block that reads or deletes files on the
server. However, it is safe to pass blocks that are interpreted by your script (a dialect).
Here is an example script that displays the post data in your browser:
#!/path/to/rebol -cs
print [
<HTML><BODY>
{Here is the posted data.}
<HR><PRE>data</PRE>
</BODY></HTML>
]
CGI is often used for processing HTML forms. The forms accept input from various fields and submit them to the Web
server as an HTML get or post method.
Here is an example that uses the CGI get to process a form and send an email as the result. There are two parts to this:
the HTML page and the CGI script.
<HTML><BODY>
<H1>CGI Emailer</H1><HR>
</FORM>
</BODY></HTML>
When the above script is submitted, it needs a CGI script to handle its results. Here is an example of such a script. This
example script decodes the form data and sends the email. It returns a confirmation page.
#!/path/to/rebol -cs
print {</BODY><HTML>}
This script should be named send.r and stored in the cgi-bin directory. It's permissions must be set to being readable and
executable by all.
When the form has been submitted by a browser, this script will run. It decodes the CGI query string into a cgi object. The
object now has email and message variables that are used for the send function. Before send is done, the email field is
converted from a string to an email datatype.
The send function is placed within a try block to catch errors if they occur while sending the email. The failed variable is
set to true if an error occurred, and the appropriate message is generated.
Other CGI examples can be found in the REBOL Script Library at http://www.rebol.com/library/library.html.
TCP ports can be opened in the same way as other REBOL protocols, using the TCP URL. To open a TCP connection to
an HTTP (Web) server on TCP port number 80:
Another way of opening a TCP connection is to provide the port specification directly. This is a substitute for using a URL
and is often quite useful:
http-port: open [
scheme: 'tcp
host: "www.example.com"
port-id: 80
]
Since ports are series, you can use the same series functions for sending and receiving data. The example below queries
the HTTP server opened in the previous example. It uses the insert function to put data into the port series which sends it
to the server:
The two newline characters are used to tell the server that the header has been sent.
The newline characters are automatically converted to CR LF sequences because the port was opened in text mode.
The server processes the HTTP request and returns a result to the port series. To read the result, use the copy function:
This loop will continue to fetch data until a none is returned from copy. This behavior differs between protocols. A none is
returned because the server closes the connection. Other protocols may send a special character to indicate the end of
the transfer.
Now that all the data has been received, HTTP port should be closed:
close http-port
This example uses the /lines refinement. The connection will now be line oriented. Data will be written and read as lines.
To read the first line from the server:
Because the port is operating in line mode, a line terminator is sent after the insert. The server response can be read with
with:
first pop
close pop
Notice that you do not supply a host name, only a port number. This type of port is called a listen port. The system now
accepts connections on port number 8001.
To wait for a connection from another machine, you wait on the listen port.
wait listen
This function does not return until a connection has been made.
NOTE: There are other options available for wait . For instance, you can wait on multiple ports or for a timeout as well.
You can now open the connection port from the machine that has contacted your system:
This returns the connection that has been made to the listen port. It is a port like all others and can now be used to
receive and send data using the insert, copy, first, and other series functions:
close connection
You are now ready for the next connection on the listen port. You can wait again and use first again to get the
connection.
When you are done with serving, you can close the listen port with:
close listen
Here is a useful REBOL server that only requires a few lines of code. This server evaluates whatever REBOL code is sent
to it. Lines of REBOL are read from the client until an error occurs. Each line must be a complete REBOL expression.
They can be of any length but must be a single line.
forever [
connection-port: first server-port
until [
wait connection-port
error? try [do first connection-port]
]
close connection-port
]
close server-port
If an error occurs, the connection is closed and the server waits for the next connection.
Here is an example of a client script that allows you to enter REBOL command lines remotely:
Here the query is used to determine if the connection was been closed due to an error.
To test your server code, connect from your own machine, rather than requiring both a server and a client. This can be
done from two separate REBOL processes or even from the same process.
To connect to your local machine, you can use a line such as:
Here is an example that makes two ports connect to each other in line mode. This is a sort of echo port since you're
sending data to yourself. It provides a good test of your code and networking:
The operation of UDP is much different than TCP. UDP is simpler, but it is essentially unreliable. There is no guarantee
that a packet will ever reach its destination. In addition, UDP has no flow control. If you send messages too quickly,
packets may be lost.
Like TCP, the wait function can be used to wait for the next packet to arrive and the copy function is used to return the
data. If there is no data, copy waits until there is. Note, however, that insert never waits.
The messages inserted here by the server are sent to the client the server last received a message from. This allows
responses to be sent for incoming messages. However, unlike TCP you do not have a continuous connection between
the machines. Each packet transfer is a separate exchange.
The client script to communicate with the above server would be:
You should know that the maximum UDP packet size depends on the operating system. 32 KB and 64 KB are common
values. In order to send larger amounts of data, you will need to buffer the data, chopping it into smaller pieces. However,
careful programming is required to make sure that each piece of the data is received. Remember that with UDP, there are
no guarantees.
Ports
Updated: 17-Jul-2001
Table of Contents
1. Overview
2. Opening a Port
2.1. The Open Function
2.2. Open Refinements
3. Closing a Port
4. Reading from a Port
5. Writing to a Port
6. Updating a Port
7. Waiting for a Port
8. Other Port Modes
8.1. Line Mode
8.2. Read and Write Only
8.3. Direct Port Access
8.4. Skipping Data
9. File Permissions
10. Directory Ports
1. Overview
Ports access external series such as files, networks, consoles, events, databases, data
encoders, and data decoders. Port data is processed using the standard REBOL series functions
as described in the Series Chapter.
Ports are used for both input and output. The type of data a port handles depends on how the port
is opened. Three types of data are possible:
2. Opening a Port
The open function initializes access to a port according to specified parameters. The function can
be supplied with a filename, a URL, or an object. In addition, there are several refinements that
will affect the open operation or the access to the port's data.
The simplest method of using open is to provide it with a filename or URL as its argument. In the
example below, a file port is opened:
The fp variable refers to the port. If the port did not open, an error will occur. If necessary, the
error can be caught with the try function.
By default the file is opened as buffered. This means that the file is accessed and modified in
memory and changes to the file are not written out until the port is closed or updated.
For files, the open function will automatically create the file if it does not already exist.
close open %somefile.txt
if exists? %somefile.txt [print "somefile exists"]
somefile exists
new data
Once a port is open, the series operations such as copy, insert, remove, clear, first, next, and
length? can be used to access and change the contents the port.
The open function accepts a number of refinements that can be used to modify its operation:
close fp
If you attempt to close a port that is not open, an error will occur.
A port that is closed can be reopened again with the open function:
open fp
The series copy function is used to read data from an open port:
print copy fp
I wanted the gold, and I sought it,I scrabbled and mucked like
a slave....
This function will wait for the port data. If you don't want to wait for the data, open the port with the
/nowait refinement.
print copy/part fp 35
Note that the second argument to copy can be a length or a position within the port.
You can use the series find and copy functions to read just part of the port's data:
a: find fp "famine"
print copy/part a find a newline
print first fp
The copy function will return none when all data have been read from a port. When running in
/nowait mode, the copy function will return an empty string if no data is available for the port.
5. Writing to a Port
If the port is buffered, the change will occur externally when the port is closed or updated (with the
update function). If the port is opened with /direct, then the change will occur immediately.
All of the insert refinements can be used on the port. For example, to write 20 spaces into a port:
You can also use the remove, clear, change, append, replace, and other series modifying
functions on the port.
remove fp
remove/part fp 20
6. Updating a Port
The update function forces a port to update its status with respect to the external device. For
example, when writing a buffered file, the update function can be used to force the data buffer out
to the file. When reading, the update function can be used to be certain that any pending data
has been read into memory.
update fp
wait port
The first example will time out in ten seconds. The second example will timeout in five seconds.
The wait function will return the port that is ready or none if the timeout occurred.
The above example will read data from the first ready port if a timeout did not occur.
To obtain a block of all ports that are ready, use the /all refinement.
This example would append data from all ready ports into a single series.
You can also use the dispatch function to evaluate a block or function based on the results of a
wait on multiple ports.
dispatch [
port1 [print "port1 awake"]
port2 [print "port2 awake"]
10 [print "timeout!"]
]
To use wait with most ports, you will need to specify the /nowait and /direct
refinements as part of the open. This indicates that the normal data access
functions should not block and that data is not buffered.
The open function allows ports to be opened for line access. In line mode, the first function will
return a line of text, rather than a character. The example below reads a file one line at a time:
The /lines refinement is also useful for Internet protocols that are line oriented.
You can use the /read refinement to open a port as read only:
Changes made to the port's buffer, are not written back to the file.
File ports opened with the /write refinement will not read the current data upon opening the port.
Closing, or updating a write only file port will cause existing data in the file to be overwritten:
The /direct refinement opens an unbuffered port. This is useful to access files a portion at a time,
such as when a file is too large to be held in memory.
Reading the data with a copy function will move the port's head forward:
print copy/part fp 40
I wanted the gold, and I sought it,^/ I
print copy/part fp 40
print head? fp
true
The copy function will return none when the port has reached its end.
Here is an example that uses direct ports to copy a file of any size:
There are two ways to skip data that exists in a port. First, you can open the port with the /skip
refinement. This open function will automatically skip to a point in the port. For example:
You can also use the skip function on the port. For files that are opened with /direct and /binary
the skip operation is identical to a file system seek operation. Data is not read into memory. This
is not possible in /string mode because the line breaks interfere with the skip size.
9. File Permissions
When files are created by REBOL, default access permissions are set. On Windows and
Macintosh systems files are created with full access privileges. On UNIX systems files are created
with the permissions set to the current umask setting.
When using open or write to access a file the /allow refinement is used to set file access
permissions.
The /allow refinement takes a block as an argument. This block can consist of any or all of the
three words read, write and execute.
To make a file read only, use open/allow, or write/allow with a read block.
To prevent any access to a file (for operating systems where this would make a difference)
provide an empty permissions block:
write/allow %file.txt []
When you open a directory, you gain direct access to the directory as a block of filenames:
CVS/
history.t
intro.t
overview.t
quick.t
close mydir
You can advance to a specific position within a directory series and remove a file with code such
as:
dir: open %.
remove next dir
close dir
remove at dir 5
clear dir
To delete all files that contain with the word "junk", you can write:
The changes made to a directory are made when the directory is closed or when it is updated. To
force the action to occur immediately use a line such as:
update dir
The method of directory access can also be used for changing the names of files. After the open,
the line:
will rename the third file in the directory. Similarly, the names of any of the files in the directory
can be changed.
Here is an example that renames all of the files in a directory by adding the word REBOL to their
names:
Parsing
Updated: 18-Jul-2001
Table of Contents
1. Overview
2. Simple Splitting
3. Grammar Rules
4. Skipping Input
5. Match Types
6. Recursive Rules
7. Evaluation
7.1. Return Value
7.2. Expressions in Rules
7.3. Copying the Input
7.4. Marking the Input
7.5. Modifying the String
7.6. Using Objects
7.7. Debugging
8. Dealing with Spaces
9. Parsing Blocks and Dialects
9.1. Matching Words
9.2. Matching Datatypes
9.3. Characters Not Allowed
9.4. Dialect Examples
9.5. Parsing Sub-blocks
10. Summary of Parse Operations
10.1. General Forms
10.2. Specifying Quantity
10.3. Skipping Values
10.4. Getting Values
10.5. Using Words
10.6. Value Matches (block parsing only)
10.7. Datatype Words
1. Overview
Parsing splits a sequence of characters or values into smaller parts. It can be used for
recognizing characters or values that occur in a specific order. In addition to providing a
powerful, readable, and maintainable approach to regular expression pattern matching,
parsing enables you to create your own custom languages for specific purposes.
The series argument is the input that is parsed and can be a string or a block. If the argument
is a string, it is parsed by character. If the argument is a block, it is parsed by value.
The rules argument specifies how the series argument is parsed. The rules argument can be
a string for simple types of parsing or a block for sophisticated parsing.
The parse function also accepts two refinements: /all and /case. The /all refinement parses
all the characters within a string, including all delimiters, such as space, tab, newline,
comma, and semicolon. The /case refinement parses a string based on case. When /case is
not specified, upper and lower cases are treated the same.
2. Simple Splitting
The parse function splits the input argument, string, into a block of multiple strings, breaking
each string wherever it encounters a delimiter, such as a space, tab, newline, comma, or
semicolon. The none argument indicates that no other delimiters other than these. For
example:
probe parse "The trip will take 21 days" none
Similarly,
In the example above, notice that the commas and semicolons have been removed from the
resulting strings.
You can specify other delimiters in the second argument to parse, which are combined with
the default delimiters (space, tab, newline, comma, semicolon).. For example, the following
code parses a telephone number adding dash (-) to the delimiters:
The next example adds equal (=) and double quote (") to the to the delimiters:
To disable the default delimiters, use the /all refinement. With the /all refinement, only the
delimiters passed in the second argument are used.
The next example parses a string based on commas only; any other delimiters are ignored.
Consequently, the spaces within the strings are not removed:
You can parse strings that contain null characters as separators (such as certain types of
data files):
To define rules, use a block to specify the sequence of the inputs. For instance, if you want to
parse a string and return the characters "the phone", you can use a rule:
To allow any number of spaces or no spaces between the words, write the rule like this:
You can indicate alternate rules with a vertical bar (|). For example:
the phone
a radio
A rule can contain blocks that are treated as sub-rules. The following line:
a phone
a radio
the phone
the radio
For increased readability, write the sub-rules as a separate block and give them a name to
help indicate their purpose:
In addition to matching a single instance of a string, you can provide a count or a range that
repeats the match. The following example provides a count:
[3 "a" 2 "b"]
aaabb
[1 3 "a" "b"]
ab aab aaab
[0 3 "a" "b"]
b ab aab aaab
Use some to specify that one or more characters are matched. Use any to specify that zero
or more characters are matched. For example, some used in the following line:
Another way to express that a character is optional is to provide an alternate choice of none:
The none is useful for specifying optional patterns or for catching error cases when no
pattern matches.
4. Skipping Input
Use skip to skip a single character, or use it with a repeat to skip over multiple characters:
["a" to "b"]
The previous example starts parsing at a and ends at b but does not include b.
The following rule finds the title of an HTML page and prints it:
REBOL Technologies
The first thru finds the title tag and goes immediately past it. Next, the input string is copied
into a variable called text until the ending tag is found (but it doesn't go past it, or the text
would include the tag).
5. Match Types
When parsing strings, these datatypes and words can be used to match characters in the
input string:
To use all of these words (except bitset, which is explained below) in a single rule, use:
<B>excellent!</B>
<B>incredible!</B>
The end specifies that nothing follows in the input stream. The entire input has been parsed.
It is optional depending on whether the parse function's return value is to be checked. Refer
to the Evaluation section below for more information.
The bitset datatype deserves more explanation. Bitsets are used to specify collections of
characters in an efficient manner. The charset function enables you to specify individual
characters or ranges of characters. For example, the line:
digit: charset "0123456789"
defines a character set that contains digits. This allows rules like:
707-467-8000
A character set can also specify ranges of characters. For instance, the digit character set
could have be written as:
Character sets can also be modified with the insert and remove functions, or combinations
of sets can be created with the union and intersect functions. This line copies the digit
character set and adds a dot to it:
6. Recursive Rules
Here is an example of rule set that parses mathematical expressions and gives a precedence
(a priority) to the math operators used:
Now we can parse many types of math expressions. The following examples return true,
indicating that the expressions were valid:
true
true
Notice in the examples that some of the rules refer to themselves. For instance, the expr rule
includes expr. This is a useful technique for defining repeating sequences and combinations.
The rule is recursive --it refers to itself.
When using recursive rules, care is required to prevent endless recursion. For instance:
creates an infinite loop because the first thing expr does is use expr again.
7. Evaluation
Normally, you parse a string to produce some result. You want to do more than just verify
that the string is valid, you want to do something as it is parsed. For instance, you may want
to pick out substrings from various parts of the string, create blocks of related values, or
compute a value.
true
false
The parse function returns true only if it reaches the end of the input string. An unsuccessful
match stops the parse of the series. If parse runs out of values to search for before reaching
the end of the series, it does not traverse the series and returns false :
false
true
true
Within a rule, you can include a REBOL expression to be evaluated when parse reaches that
point in the rule. Parentheses are used to indicate such expressions:
found phonetrue
The example above parses the string a phone and prints the message found phone after the
match is complete. If the strings a or phone are missing and the parse can not be done, the
expression is not evaluated.
Expressions can appear anywhere within a rule, and multiple expressions can occur in
different parts of a rule. For instance, the following code prints different strings depending on
what inputs were found:
parse string [
"a" | "the"
to "phone" (print "answer") |
to "radio" (print "listen") |
to "tv" (print "watch")
]
answer
parse string [
"a" | "the"
to "phone" (print "answer") |
to "radio" (print "listen") |
to "tv" (print "watch")
]
listen
Here is an example that counts the number of times the HTML pre-format tag appears in a
text string:
count: 0
page: read http://www.rebol.com/dictionary.html
parse page [any [thru <pre> (count: count + 1)]]
print count
777
The most common action done with parse is to pick up parts of the string being parsed. This
is done with copy, and it is followed by the name of a variable to which you want to copy the
string. The following example parses the title of a web page:
REBOL/Core Dictionary
The example works by skipping over text until it finds the <title> tag. That's where it
starts making a copy of the input stream and setting a variable called text to hold it. The copy
operation continues until the closing <title> tag is found.
The copy action also can be used with entire rule blocks. For instance, for the rule:
the heading string contains the entire H1, H2, or H3 string. This also works for large multi-
block rules.
The copy action makes a copy of the substring that it finds, but that is not always desirable.
In some cases, it is better to save the current position of the input stream in a variable.
NOTE: The copy word as used in parse is different from the copy function used in REBOL
expressions. Parse uses a dialect of REBOL, and copy has a different meaning within that
dialect.
In the following example, the begin variable holds a reference to the page input string just
after <title>. The ending refers to the page string just before . These variables can be
used in the same way as they would be used with any other series.
parse page [
thru <title> begin: to </title> ending:
(change/part begin "Word Reference Guide" ending)
]
You can see the above parse expression actually changed the contents of the title:
Here is another example that marks the position of every table tag in an HTML file:
NOTE: The current position in the input string can also be modified. The next section
explains how this is done.
Now that you know how to obtain the position of the input series, you also can use other
series functions on it, including insert, remove, and change. To write a script that replaces
all question marks (?) with exclamation marks (!), write:
The skip at the tail advances the input over the new character, which is not necessary in this
case, but it is a good practice.
As another example, to insert the current time everywhere the word time appears in some
text, write:
str: "at this time, I'd like to see the time change"
parse str [
some [to "time"
mark:
(remove/part mark 4 mark: insert mark now/time)
:mark
]
]
print str
Notice the :mark word used above. It sets the input to a new position. The insert function
returns the new position just past the insert of the current time. The word :mark is used to set
the input to that position.
When parsing large grammar from a set of rules, variables are used to make the grammar
more readable. However, the variables are global and may become confused with other
variables that have the same name somewhere else in the program.
The solution to this problem is to use an object to make all the rule words local to a context.
For instance:
As rules are written, there are times debugging is needed. Specifically, you may want to
know how far you got in the parsing of a rule.
The trace function can be used to watch the parse operation progress, but this can output
thousands of lines that are difficult to review.
A better way is to insert debugging expressions into the parse rules. As an example, to
debug the rule:
insert a the print function after key sections to monitor your progress through the rule:
Another approach is to print out part of the input string as the parse happens:
[
to "<IMG" here: (print here)
"SRC" "=" here: (print here)
filename here: (print here) ">"
]
[
to "<IMG" here
"SRC" "=" here
filename here ">"
]
The copy function can also be used to indicate what substrings were parsed as the rule was
handled.
8. Dealing with Spaces
The parse function normally ignores all intervening whitespace between patterns that it
scans. For instance, the rule:
abc
a bc
ab c
a b c
a b c
To enforce a specific spacing convention, use parse with the /all refinement. In the
preceeding example, this refinement causes parse to only match the first case (abc).
Specifying the / all refinement forces every character in the input stream to be dealt with,
including the default delimiters, such as space, tab, newline.
To handle spaces in your rules, create a character set that specifies the valid space
characters:
For more sophisticated grammars, create a character set that lets you scan a string up to a
space character.
The preceding example builds a block of all of its words. The complement function inverts
the character set. Now it contains everything except the spacing characters you defined
earlier. The non-space character set contains all characters except space characters. The to-
space rule accepts one or more characters up to a space character or the end of the input
stream. The main rule expects to begin with a word, copy that word up to a space, then skip
the space character and begin the next word.
Block parsing is the easiest way to create REBOL dialects. Dialects are sub-languages of
REBOL that use the same lexical form for all data types but allow a different ordering of the
values within a block. The values do not need to conform to the normal order required by
REBOL function arguments. Dialects are able to provide greater expressive power for
specific domains of use. For instance, the parser rules themselves are specified as a dialect.
When parsing a block, to match against a word specify the word as a literal:
'name
'when
'empty
You can match a value of any datatype by specifying the data type word. See Datatype
Matches below.
NOTE: Don't forget the "!" that is part of the name or an error will be generated.
The parse operations allowed for blocks are those that deal with specific characters. For
instance, a match cannot be specified to the first letter of a word or string, nor to spacing or
newline characters.
Notice that a specific word can be matched by using its literal word in the rule (as in the case
of 'when ). A datatype can be specified rather than a value, as in the lines above containing
time!. In addition, a variable can be set to a value with the set operation.
rule: [some [
'when set time time! |
'where set place string! |
'who set persons [word! | block!]
]]
parse [
who Fred
where "Downtown Center"
when 9:30
] rule
print [time place persons]
This example could have used variable assignment, but it illustrates how to provide alternate
input ordering.
rule: [
set count integer!
set str string!
(loop count [print str])
]
parse [3 "great job"] rule
parse [3 "hut" 1 "hike"] [some rule]
rule: [
set action ['buy | 'sell]
set number integer!
'shares 'at
set price money!
(either action = 'sell [
print ["income" price * number]
total: total + (price * number)
][
print ["cost" price * number]
total: total - (price * number)
]
)
]
total: 0
parse [sell 100 shares at $123.45] rule
print ["total:" total]
total: 0
parse [
sell 300 shares at $89.08
buy 100 shares at $120.45
sell 400 shares at $270.89
] [some rule]
print ["total:" total]
9.5. Parsing Sub-blocks
When parsing a block, if a sub-block is found, it is treated as a single value that is of the
block! datatype. However, to parse a sub-block, you must invoke the parser recursively on
the sub-block. The into word provides this capability. It expects that the next value in the
input block is a sub-block to be parsed. This is as if a block! datatype had been provided. If
the next value is not a block! datatype, the match fails and into looks for alternates or exits
the rule. If the next value is a block, the parser rule that follows the into word is used to begin
parsing the sub-block. It is processed in the same way as a sub-rule.
rule: [
set date date!
set info into [string! time!]]
]
data: [10-Jan-2000 ["Ukiah" 10:30]]
print parse data rule
print info
probe items
Operator Description
| alternate rule
[block] sub-rule
Operator Description
Operator Description
Operator Description
Operator Description
Operator Description
Word Description
Number Values
Series Values
Other Values
Number Values
Decimal
Concept
The decimal! data type includes 64-bit standard IEEE floating point numbers. They are distinguished from integer numbers by
a decimal point.
Format
Decimal values are a sequence of numeric digits, followed by a decimal point, which can be a period (.) or a comma (,),
followed by more digits. A plus (+) or minus (-) immediately before the first digit indicates sign. Leading zeros before the
decimal point are ignored. Extra spaces, commas, and periods are not allowed.
1.23
123.
123.0
0.321
0.123
1234.5678
A comma can be used in place of a period to represent the decimal point (which is the custom in some countries):
1,23
0,321
1234,5678
Use a single quote (`) to separate the digits in long decimals. Single quotes can appear anywhere after the first digit in the
number, but not before the first digit.
100'234'562.3782
100'234'562,3782
Scientific notation can be used to specify the exponent of a number by appending the number with the letter E or e followed by
a sequence of digits. The exponent can be a positive or negative number.
1.23E10
1.2e007
123.45e-42
56,72E300
-0,34e-12
0.0001e-001
Decimal numbers span from 2.2250738585072e-308 up to 1.7976931348623e+308 and can contain up to 15 digits of precision.
Creation
Use the to-integer function to convert a string!, integer! , block! , or a decimal! data type to a decimal number:
123.45
123
-1.23E+47
1.23E-43
-123.8
12.3
If a decimal and integer are combined in an expression, the integer is converted to a decimal number:
probe 1.2 + 2
3.2
probe 2 + 1.2
3.2
true
probe 1 > 1.01
false
Related
true
Use the form , print , and mold functions with an integer argument to print a decimal value in its simplest form:
For example,
123.4
2.22222222222222E+15
print 1.00001E+5
100001
Single quotes (`) and a leading plus sign (+) do not appear in decimal output:
print +1'100'200.222'112
1100200.222112
Integer
Concept
The integer! data type includes 32-bit positive and negative numbers and zero. Unlike decimal numbers, integers do not contain
a decimal point.
Format
Integer values consist of a sequence of numeric digits. A plus (+) or minus (-) immediately before the first digit indicates sign.
(There cannot be a space between the sign and the first digit.) Leading zeros are ignored.
Do not use commas or periods in integers. If a comma or period is found within an integer it is interpreted as a decimal value. .
However, you can use a single quote (`) to separate the digits in long integers. Single quotes can appear anywhere after the first
digit in the number, but not before the first digit.
2'147'483'647
Creation
Use the to-integer function to convert a string!, logic! , decimal! , or integer! data type to an integer:
123
123
123
-123
If a decimal and integer are combined in an expression, the integer is converted to a decimal:
probe 1.2 + 2
3.2
probe 2 + 1.2
3.2
true
true
Related
Use integer? to determine whether a value is an integer! data type.
true
Use the form , print , and mold functions with an integer argument to print a integer value as a string:
123
123
print 123
123
Integers that are out of range or cannot be represented in 32 bits are flagged as an error.
Series Values
Binary
Concept
Binary values hold binary data of any arbitrary type. Any sequence of bytes can be stored, such as an image, audio, executable
file, compressed data, and encrypted data. The source format for binary data can be base-2 (binary), base-16 (hex), and base-64.
The default base for binary data in REBOL is base-16.
Format
Binary strings are written as a number sign (#) followed by a string enclosed in braces. The characters within the string are
encoded in one of several formats as specified by an optional number prior to the number sign. Base-16 is the default format.
2#{10010110110010101001011011001011} ; base-2
64#{LmNvbSA8yw9CB0aGvXmgUkVCu2Uz934b} ; base-64
Spaces, tabs and newlines are permitted within the string. Binary data can span multiple lines.
probe #{
3A
18
92
56
}
#{3A189256}
Strings that are missing the correct number of characters to create a correct binary result are padded on the right.
Creation
The to-binary function converts data to the binary! data type at the default base set in system/options/binary-base :
#{313233}
#{746F64617920697320746865206461792E2E2E}
#{01}
#{0B}
Converting a series of integers into a binary, returns the bit conversion for each integer concatenated into a single binary value:
probe to-binary [1 1 1 1]
#{01010101}
Related
true
true
Closely related to working with binary! data types are the functions enbase and debase . The enbase function converts strings
to their base-2, base-16 or base-64 representations as strings. The debase function converts enbased strings to a binary value of
the base specified in system/options/binary-base .
Block
Concept
Blocks are groups of values and words. Blocks are used everywhere, from a script itself to blocks of data and code provided in a
script.
Block values are indicated by opening and closing square brackets ([]) with any amount of data contained between them.
[] ; empty block
woodsmen: [
Blocks are also a type of series, and thus anything that can be done with a series can be done with a block value.
[
"Grizzly" "Adams" grizzly@adams.dom]
append woodsmen [
probe woodsmen
[
"Paul" "Bunyuan" paul@bunyuan.dom
"Grizzly" "Adams" grizzly@adams.dom
"Davey" "Crocket" davey@crocket.dom
"John" "Muir" john@muir.dom
]
do blk
data in a block
blks: [
block one
block two
block three
Format
Blocks can contain any number of values or no values at all. They can extend over multiple lines and can include any type of
value, including other blocks.
An empty block:
[ ]
A block of integers:
[24 37 108]
A REBOL header:
REBOL [
Date: 31-Dec-1998
print time
]
Words in a block need not be defined:
false
Blocks allow any number of lines, spaces, or tabs. Lines and spaces can be placed anywhere within the block, so long as they do
not divide a single value.
Creation
[luke@rebol.com]
Related
true
As blocks are a subset of the series! pseudotype, use series? to check this:
true
Using form on a block value creates a string from the contents contained in the block:
123 10:30
Using mold on a block value creates a string from the block value and it's contents, thus allowing it to be reloaded as a REBOL
block value:
[123 10:30]
Closely related data types are hash! and list! . They are used in much the same way as block values, but have special
capabilities. List values are designed to handle modification of lists more quickly than block values, and hash values are
designed handle data lookup and hash indexing of data. These are useful when dealing with large data sets.
Email
Concept
An email address is a data type. The email! data type allows for easy expression of email addresses:
emails: [
john@keats.dom
lord@byron.dom
edger@guest.dom
alfred@tennyson.dom
Email is also one of the series! data types, so the same rules that apply to series apply to emails:
john@doe.dom
Format
The standard format of an email address is a name, followed by an at sign ( @ ), followed by a domain. An email address can be
of any length, but must not include any of restricted characters, such as square brackets, quotes, braces, spaces, newlines, etc..
info@rebol.com
123@number-mail.org
my-name.here@an.example-domain.com
Access
Refinements can be used with an email value to get the user name or domain. The refinements are:
email: luke@rebol.com
probe email/user
luke
probe email/host
rebol.com
Creation
info@rebol.com
info@rebol.com
info@rebol.com
probe to-email [user some long domain name out there dom]
user@some.long.domain.name.out.there.dom
Related
true
As emails are a subset of the series! pseudotype, use series? to determine whether the value is a series:
true
#"@"
File
Concept
The file! data type can be a file name, directory name, or directory path.
%file.txt
%directory/
%directory/path/to/some/file.txt
File values are a subset of series, and thus can be manipulated as a series:
%path2/file.txt
f: %dir/path/file.txt
%dir/file.txt
Format
load %image.jpg
Unusual characters in file names must be encoded with a % hexadecimal number, which is an Internet convention. A file name
with a space (hexadecimal 20) would look like:
probe %cool%20movie%20clip.mpg
%cool%20movie%20clip.mpg
print %cool%20movie%20clip.mpg
%cool%20movie%20clip.mpg
The standard character for separating directories in a path is the forward slash (/), not the backslash (\). However, the REBOL
language automatically converts backslashes found in file names to forward slashes:
probe %\some\path\to\some\where\movieclip.mpg
%/some/path/to/some/where/movieclip.mpg
Creation
%testfile
When passed a block, elements in the block are concatenated into a file path with the final element used as the file name:
%some/path/to/a/file/the-file.txt
Related
true
As files are a subset of the series! pseudotype, use series? to check this:
true
Hash
Concept
Hash is a block that is specially organized to make finding data faster. When searching is performed on a hash block, the search
is performed by using a hash table for lookup. For large blocks, this can speed searches by hundreds of times.
Format
Hash blocks must be constructed by using make or to-hash . They have no lexical format.
Creation
Convert a block:
two
Convert various values:
Related
true
As hashes are a subset of the series! pseudotype, use series? to check this:
true
Forming a hash value creates a string from the contents contained in the hash:
Molding a hash value creates a string of the hash value itself and its contents, thus allowing it to be reloaded as a REBOL hash
value:
Image
Concept
The image! data type is a series that holds RGB images. This data type is used with REBOL/View.
The image formats supported are GIF, JPEG, and BMP. The loaded image can be manipulated as a series.
Format
Images are normally loaded from a file. However, they can be expressed in source code as well by making an image. The block
provided includes the image size and its RGB data.
B34533B44634B44634B54735B7473
84836B84836B84836BA4837BA4837
BC4837BC4837BC4837BC4837BC483 ...
Creation
Images can also be made from snapshots of a face object. This is also done using make or to-image:
Use load to load an image file. If the image's format is not supported, it will fail to load.
Loading an image:
Related
Use the /size refinement to return the pixel size of an image as a pair value:
probe img/size
The pixel values of an image are obtained using pick and changed using poke . The value returned by pick is an RGB tuple
value. The value replaced with poke also should be a tuple value.
Issue
Concept
An issue! is a series of characters used to sequence symbols or identifiers for things like telephone numbers, model numbers,
serial numbers, and credit card numbers.
Issue values are a subset of series, and thus can be manipulated as series:
#555
Format
Issues start with a number sign (#) and continue until the first delimiting character (such as a space) is reached.
#707-467-8000
#A-0987654321-CD-09876
#1234-5678-4321-8765
#MG82/32-7
Values that contain delimiting characters should be written as strings rather than issues.
Creation
#1234-56-7890
Related
true
As issues are a subset of the series pseudotype, use series? to check this:
true
The form function returns an issue as a string without the number sign (#):
probe form #1234-56-7890
1234-56-7890
The mold function returns an issue as a string that can be read by REBOL as an issue value:
#1234-56-7890
The print function prints an issue to standard output after doing a reform on it:
print #1234-56-7890
1234-56-7890
List
Concept
Lists are linked list blocks that allow for faster and more efficient insertion and removal of their values. They can be used in
cases where a large number of insertions or removals are being performed on large blocks.
Format
List blocks must be constructed by using make or to-list . They have no lexical format.
Lists values are not a direct substitute for blocks. There are a couple of differences between blocks and lists:
Inserting into a list modifies its reference to just after the point of insertion.
Removing the element currently referenced in a list causes the reference to reset to the tail of the list
The following examples show the difference in behavior between inserting into a list and a block.
blk: [1 2 3]
lst: to-list [1 2 3]
insert blk 0
insert lst 0
Looking at the word after the block and list after insertion. Notice blk points to the head, as before the insertion of 0 , but lst
points to just after the point of insertion:
print blk
0123
print lst
123
0123
The following examples show the difference in behavior between removing an element from a list and a block.
blk: [1 2 3]
lst: to-list [1 2 3]
remove blk
remove lst
Looking at the word after removal of the value. Notice lst now points to the tail of the series:
print blk
23
true
23
If you don't want the word to be at the tail after removing a value, step forward and remove the value behind the current index.
The following examples depicts this.
Initializing a list:
lst: to-list [1 2 3]
Stepping forward and removing the value behind the current index:
probe lst
make list! [2 3]
Creation
Convert a block:
Related
true
Since lists are a subset of the series! data type, use series? to check whether a list is a series:
true
Using form on a list value creates a string from the contents contained in the list:
Using mold on a list value creates a string of the list value itself and it's contents, thus allowing it to be reloaded as a REBOL
list value:
Paren
Concept
A paren! data type is a block that is immediately evaluated. It is identical to a block in every way, except that it is evaluated
when it is encountered and its result is returned.
When used within an evaluated expression, a paren! allows you to control the order of evaluation:
print 1 + (2 * 3)
print 1 + 2 * 3
9
The value of a paren! can be accessed and modified in the same way as any block. However, when referring to a paren! , care
must be taken to prevent if from being evaluated. If you store a paren in a variable, you will need to use a get-word form
(:word) to prevent it from being evaluated.
Parens are a type of series, thus anything that can be done with a series can be done with paren values.
paren!
probe :paren
(10 + 5 * 1 + 2 * 3 / 4)
print paren
12.75
Format
Parens are identified by their open and closing parenthesis. They can span multiple lines and contain any data, including other
paren values.
Creation
insert :paren 10
insert :paren `+
insert :paren 20
print :paren
20 + 10
print paren
30
(123 456)
(123 456)
Related
(3 + 3)
true
As parens are a subset of the series! pseudotype, use series? to check this:
true
Using form on a paren value creates a string from the contents contained in the paren:
3+3
Path
Concept
Paths are a collection of words and values delineated with forward slashes (/). Paths are used to navigate to or find something.
The words and values of a path are called refinements, and they are combined to provide a means of navigating through a
value or function.
Paths can be used on blocks, files, strings, lists, hashes, functions, and objects. How a path operates depends on the data type
being used.
Paths can be used to select values from blocks, pick characters from strings, access variables in objects, refine the operation of a
function:
USA/CA/Ukiah/size (block selection)
The example below shows the simplicity of using a path to access a mini-database created from a few blocks:
towns: [
Hopland [
phone #555-1234
web http://www.hopland.ca.gov
Ukiah [
phone #555-4321
web http://www.ukiah.com
email info@ukiah.com
print towns/ukiah/web
http://www.ukiah.com
Path Relationship shows the relationship of paths corresponding with type words, type tests, and conversions:
Path Relationship
Examples of Paths
obj/hello
hello! hello!
probe obj/text
Function refinements:
hello/again
hello again!
USA: [
CA [
Ukiah [
population 15050
Willits [
population 9935
print USA/CA/Ukiah/population
15050
print USA/CA/Willits/elevation
1350 feet
Pick elements from series and embedded series by their numeric position:
string-series: "abcdefg"
probe string-series/4
#"d"
probe block-series/3
Jake
probe block-series/6
43
probe block-with-sub-series/1/2
#"b"
probe block-with-sub-series/2/2
probe block-with-sub-series/2/4/2
The words supplied as paths are symbolic and therefore unevaluated. This is necessary to allow the most intuitive form for
object referencing. To use a word's reference, an explicit word value reference is required:
city: 'Ukiah
probe USA/CA/:city
[
population 15050
elevation "610 feet"
]
Paths in blocks, hashes, or objects are evaluated by matching the word at the top level of the path, and verifying the word as a
block! , hash! or object! value. Then the next word in the path is sought as a word expressed in the block, hash or object and an
implicit select is performed. The value following the word matched is returned. When the returned value is a block, hash, or
object, the path can be extended:
[
Ukiah [
population 15050
elevation "610 feet"
]
Willits [
population 9935
elevation "1350 feet"
]
]
probe USA/CA/Willits
[
population 9935
elevation "1350 feet"
]
probe USA/CA/Willits/population
9935
When a word is used in a path that does not exist at the given point in the structure, an error is produced:
probe USA/CA/Mendocino
probe USA/CA/Willits
[
population 9935
elevation "1 foot, after the earthquake"
]
probe obj
make object! [
text: "yes, I do believe in magic."
]
USA: [
CA [
probe obj/USA/CA/population
too many
"hello again"
] [
"oh, hello"
obj/hello/again
hello again
Paths are a type of series, thus anything that can be done with a series can be done with path values:
num 55
] ] ]
path: 'root/sub1/sub2/word
probe :path
root/sub1/sub2/word
In the previous example, the :path notation was used to get the path itself, not the path's value:
probe path
sub2/word
probe :path
root/sub1/sub2/num
probe path
55
Format
Paths are expressed relative to a root word by providing a number of refinements, each separated by a forward slash (/). These
refinements can be words or values. Their specific interpretation vary depending on the data type of the root value.
The words supplied as refinements in paths are symbolic and are not evaluated. This is necessary to allow the most intuitive
form for object referencing. To use a word's reference, an explicit word value reference is required:
root/:word
This example uses the value of the variable, rather than it name.
Creation
print :path
test/this
The to-path function converts data to the path! data type:
root/sub
root/sub
The to-set-word function converts other values to the set-word data type.
root/sub:
The to-lit-word function converts other values to the lit-word data type.
'root/sub
Related
Use path? , set-path? , and lit-path? to determine the data type of a value.
false
blk2: [blk/sub1/sub2/word: 2]
it is set
true
As paths are a subset of the series! pseudotype, use series? to check this:
true
root/sub
Use mold on a path value creates a string of the path value itself, thus allowing it to be reloaded as a REBOL path value:
probe mold pick [root/sub] 1
root/sub
String
Concept
Strings are a series of characters. All operations performable on series values can be performed on strings.
Format
String values are written as a sequence of characters surrounded by double quotes " " or braces {}. Strings enclosed in double
quotes are restricted to a single line and must not contain unprintable characters.
Strings enclosed in braces are used for larger sections of text that span multiple lines. All of the characters of the string,
including spaces, tabs, quotes, and newlines are part of the string.
purposes.}
Braces are counted within the string, so a string can include other braces as long as the number of closing braces matches the
number of opening braces.
You can include special characters and operations in strings by prefixing them with a caret (^). Special characters include:
Special Characters
Character Definition
" Inserts a double quote (").
- Inserts a tab.
Creation
The to-string function converts data of other data types to a string! data type:
"29-Feb-2000"
"123456.789"
"888-555-2341"
Converting a block of data to a string with to-string has the effect of doing a rejoin , but without evaluating the block's
contents:
"123456"
Related
true
true
The functions form and mold are closely related to strings, as they create strings from other data types. The form function
makes a human readable version of a specified data type, while mold makes a REBOL readable version.
Tag
Concept
Tags are used in HTML and other markup languages to indicate how text fields are to be treated. For example, the tag <HTML>
at the beginning of a file indicates that it should be parsed by the rules of the Hypertext Markup Language. A tag with a forward
slash (/), such as </HTML> , indicates the closing of the tag.
probe a-tag
<img src="mypic.jpg">
probe a-tag
Format
A valid tag is any text that begins with an open angle bracket (<).
<a href="index.html">
Creation
<title>
Use build-tag to construct tags, including their attributes. The build-tag function takes one argument, a block. In this block, the
first word is used as the tag name and the remaining words are processed as attribute value pairs:
<a href="http://www.rebol.com/">
probe build-tag [
Related
true
As tags are a subset of the series pseudotype, use series? to check this:
true
{<a href="http://www.rebol.com/">}
{<a href="http://www.rebol.com/">}
The print function prints a tag to standard output after doing a reform on it:
URL
Concept
URL is an acronym for Uniform Resource Locator, an Internet standard used to access resources such as web pages, images,
files, and email across the network. The best known URL scheme is that used for web locations such as
http://www.REBOL.com .
URL values are a subset of series, and thus can be manipulated as series:
url: http://www.rebol.com/reboldoc.html
%reboldoc.html
Format
The first part of a URL indicates its communications protocol, called a scheme . The language supports several schemes,
including: web pages ( HTTP: ), file transfer ( FTP: ), newsgroups ( NNTP: ), email ( MAILTO: ), files ( FILE: ), finger
( FINGER: ), whois ( WHOIS: ), small network time ( DAYTIME: ), post office ( POP: ), transmission control ( TCP: )
and domain name service ( DNS: ). These scheme names are followed by characters that are dependent on which scheme being
used.
http://host.dom/path/file
ftp://host.dom/path/file
nntp://news.some-isp.net/some.news.group
mailto:name@domain
file://host/path/file
finger://user@host.dom
whois://rebol@rs.internic.net
daytime://everest.cclabs.missouri.edu
pop://user:passwd@host.dom/
tcp://host.dom:21
dns://host.dom
Some fields are optional. For instance, the host can be followed by a port number if it differs from the default. An FTP URL
supplies a default password if one is not specified:
ftp://user:password@host.dom/path/file
Characters in a URL must conform to Internet standards. Restricted characters must be encoded in hexadecimal by preceding
them with the escape character %:
probe http://www.somesite.dom/odd%28dir%29/odd%7Bfile%7D.txt
http://www.somesite.dom/odd%28dir%29/odd%7Bfile%7D.txt
print http://www.somesite.dom/odd%28dir%29/odd%7Bfile%7D.txt
http://www.somesite.dom/odd(dir)/odd{file}.txt
Creation
The to-url function converts blocks to the url! data type, the first element in the block is the scheme, the second element is the
domain (with or without user:pass and port) the remaining elements are the path and file:
http://www.rebol.com/reboldoc.html
http://www.rebol.com/examples/websend.r
http://usr:pass@host.com:80/%28path%29/index.html
Related
true
As urls are a subset of the series pseudotype, use series? to check this:
true
Other Values
Character
Concept
Characters are not strings; they are the individual values from which strings are constructed. A character can be a printable,
unprintable, or a control character.
Format
A char! value is written as a number sign (#) followed by a string enclosed in double quotes. The number sign is necessary to
distinguish a character from a string:
#"R" ; the single character: R
Characters can include escape sequences that begin with a caret(^)and are followed by one or more characters of encoding. This
encoding can include the characters #"^ A " to #"^ Z " for control A to control Z (upper and lower case are the
same):
#"^A" #"^Z"
Control Characters
Character Definition
#"(null)" or #"@" null (zero)
#"(esc)" escape
#"(back)" backspace
#"(del)" delete
Creation
Characters can be converted to and from other data types with the to-char function:
#"a"
#"z"
Characters follow the ASCII standard and can be constructed by specifying a character's numeric equivalent:
probe to-char 65
#"A"
probe to-char 52
#"4"
#"4"
Another method of obtaining a character is to get the first character from a string:
#"A"
While characters in strings are not case sensitive, individual characters are case sensitive:
true
false
Related
false
true
Use the form function to print a character without the number sign:
"A"
Use mold on to print a character with the number sign and double quotes (and escape sequences for those characters that require
it.):
{#"A"}
Date
Concept
Around the world, dates are written in a variety of formats. However, most countries use the day-month-year format. One
of the few exceptions is the United States , which commonly uses a month-day-year format. For example, a date written
numerically as 2/1/1999 is ambiguous. The month could be interpreted as either February or January. Some countries use a dash
(-), some use a forward slash (/), and others use a period (.) as a separator. Finally, computer people often prefer dates in the
year-month-day (ISO) format so they can be easily sorted.
Format
The REBOL language is flexible, allowing date! data types to be expressed in a variety of formats. For example, the first day of
March can be expressed in any of the following formats:
probe 1/3/1999
1-Mar-1999
probe 1-3-1999
1-Mar-1999
1-Mar-1999
The year can span up to 9999 and down to 1. Leap days (February 29) can only be written for leap years:
probe 29-2-2000
29-Feb-2000
The fields of dates can be separated with forward slashes (/) or dashes (-). Dates can be written in either a year-month-day
format or a day-month-year format:
probe 1999-10-5
5-Oct-1999
probe 1999/10/5
5-Oct-1999
probe 5-10-1999
5-Oct-1999
probe 5/10/1999
5-Oct-1999
Because the international date formats that are not widely used in the USA, a month name or month abbreviation can also be
used:
probe 5/Oct/1999
5-Oct-1999
probe 5-October-1999
5-Oct-1999
probe 1999/oct/5
5-Oct-1999
When the year is the last field, it can be written as either a four digit or two digit number:
probe 5/oct/99
5-Oct-1999
probe 5/oct/1999
5-Oct-1999
However, it is preferred to write the year in full. Otherwise, problems occur with date comparison and sorting operations. While
two digits can be used to express a year , the interpretation of a two-digit year is relative to the current year and is only valid for
50 years in the future or in the past:
28-Feb-1966
12-Mar-2020
11-Mar-2045
To represent dates in the first century (which is rarely done because the Gregorian calendar did not exist), use leading zeros to
represent the century (as in 9-4-0029 ).
Dates can also include an optional time field and an optional time zone. The time is separated from the date with a forward slash
(/). The time zone is appended using a plus (+) or minus (-), and no spaces are allowed. Time zones are written as a time shift
(plus or minus) from GMT. The resolution of the time zone is to the half hour. If the time shift is an integer, it is assumed to be
hours:
probe 4/Apr/2000/6:00+8:00
4-Apr-2000/6:00+8:00
probe 1999-10-2/2:00-4:00
2-Oct-1999/2:00-4:00
probe 1/1/1990/12:20:25-6
1-Jan-1990/12:20:25
10
99
Access
Refinements can be used with a date value to get any of its defined fields:
Refinement Description
/day Gets the day.
some-date: 29-Feb-2000
probe some-date/day
29
probe some-date/month
probe some-date/year
2000
Tue
When a time is present, the time related refinements can be used. The /hour , /minute and /second refinements are used with
the /time refinement that isolates the time segment of the date value for them to work on:
lost-time: 29-Feb-2000/11:33:22.14-8:00
probe lost-time/time
11:33:22.14
probe lost-time/time/hour
11
probe lost-time/time/minute
33
probe lost-time/time/second
22.14
probe lost-time/zone
-8:00
Creation
5-Oct-1999
5-Oct-1999/10:30
5-Oct-1999/10:30-8:00
[!Note When converting to a date! , the year must be specified as four digits.
probe 5-Oct-1999 + 1
6-Oct-1999
probe 5-10-1999 - 10
25-Sep-1999
6-Oct-1999/4:00
Related
true
The related function to-idate returns a standard Internet date string. The Internet date format is day, date, month, year, time (24-
hour clock), and time zone offset from GMT.
The now function returns the current date and time in full format including the time zone offset:
probe now
30-Jun-2000/14:42:26-7:00
Logic
Concept
The logic! data type consists of two states representing true and false . They are often returned from comparisons such as:
age: 100
true
time: 10:31:00
false
true
The logic! data type is most commonly used as parameters to conditional functions such as if , while , and until :
Centennial human
wait [0:10]
Format
Normally, logic values are retrieved from the evaluation of comparison expressions. However, words can be set to a logic value
and used to turn the word on or off:
print-me: false
turned off
print-me: true
turned on
The false value is not equivalent to integer zero or none . However, in conditional expressions false and none have the same
effect:
print-me: none
turned off
Just about any value assigned to a word has the same effect as true:
turned on
print-me: 11-11-1999
turned on
true
on ;same as true
false
no ;same as false
So, instead of true and false , when it makes sense, the words on and off , or yes and no can be used instead:
print-me: yes
turned on
print-me: no
turned off
print-me: on
turned on
print-me: off
turned off
Creation
The to-logic function converts integer! or none! values to the logic! data type:
probe to-logic 0
false
true
false
probe to-logic []
true
true
false
Related
probe logic? 1
false
probe logic? on
true
true
Use the functions form , print , and mold to print a logic value:
true
false
print true
true
Money
Concept
There is a wide variety of international symbols for monetary denominations. Some symbols are used before the amount and
some after. As a standard for representing international monetary values, the REBOL language uses the United States monetary
format , but allows the inclusion of specific denominations.
Format
The money! data type uses standard IEEE floating point numbers allowing up to 15 digits of precision including cents.
The language limits the length to 64 characters. Values that are out of range or cannot be represented in 64 characters are
flagged as an error.
Monetary values are prefixed with an optional currency designator, followed by a dollar sign ($). A plus (+) or minus (-) can
appear immediately before the first character (currency designator or dollar sign) to indicate sign.
$123
-$123
$123.45
US$12
US$12.34
-US$12.34
$12,34
-$12,34
DEM$12,34
To break long numbers into readable segments, a single quote (`) can be placed anywhere between two digits within the amount,
but not before the amount.
probe $1'234.56
$1234.56
probe $1'234'567,89
$1234567.89
Do not use commas and periods to break up large amounts, as both these characters
represent decimal points.
The money! data type is a hybrid data type. Conceptually money is scalar--an amount of money. However, because the
currency designation is stored as a string, the money! data type has two elements:
string! - The currency designator string, which can have 3 characters maximum.
my-money: USD$12345.67
USD
12345.67
none
my-money: $12345.67
""
12345.67
Various international currencies can be specified in the currency designator, such as:
my-money: DKM$12'345,67
DKM
12345.67
Creation
Use the to-money function to convert money from a string! , integer! , decimal! , or block! .
$123.00
$123.00
DEM$12.34
USA$12.34
Money can be added, subtracted, and compared with other money of the same currency. An error occurs if a different currency
is used for such operations (automatic conversions are not currently supplied).
$110.00
$50.00
true
Money can be multiplied and divided by integers and decimals. Money can also be divided by money, resulting in an integer or
decimal.
probe $100 + 11
$111.00
probe $100 / 4
$25.00
probe $100 * 5
$500.00
$79.50
probe 10 + $1.20
$11.20
probe 10 - $0.25
$9.75
probe 10 * $0.75
$7.50
Related
true
Use the form , print , and mold functions with a money argument to print a money value with the currency designator and
dollar sign ($), as a decimal number with two digits of decimal precision.
USD$12.34
USD$12.34
print USD$12.34
USD$12.34
None
Concept
The none! data type contains a single value that represents nothing or no value.
The concept of none is not the same as an empty block, empty string, or null character. It is an actual value that represents non-
existence.
A none! value can be returned from various functions, primarily those involving series (for example, pick and find ).
The REBOL word none is defined as a none! data type and contains a none! value. The word none is not equivalent to zero or
false . However, none is interpreted as false by many functions.
A none! value has many uses such as a return value from series functions like pick , find and select:
email-database: [
"Bobby" bob@rebol.com 40
"Linda" none 23
"Sara" sara@rebol.net 33
]
secure none
Format
Although none is not equivalent to zero or false , it is valid within conditional expressions and has the same effect as false :
none
Creation
Related
print none? 1
false
true
The form , print , and mold functions print the value none when passed a none argument.
none
none
print none
none
Pair
Concept
A pair! data type is used to indicate spatial coordinates, such as positions on a display. They are used for both positions and
sizes. Pairs are used primarily in REBOL/View.
Format
100x50
1024x800
-50x200
Creation
Use to-pair to convert block or string values into a pair data type:
p: to-pair "640x480"
probe p
640x480
probe p
800x600
Related
true
true
100x200 + 10x20
10x20 * 2x4
100x30 / 10x3
100x100 * 3
10x10 + 3
pair: 640x480
480
All pair values support the /x and /y refinements. These refinements allow the viewing and manipulation of individual pair
coordinates.
probe pair/x
640
probe pair/y
480
pair/x: 800
pair/y: 600
probe pair
800x600
Time
Concept
The REBOL language supports the standard expression of time in hours, minutes, seconds, and subseconds. Both positive and
negative times are permitted.
The time! data type uses relative rather than absolute time. For example, 10:30 is 10 hours and 30 minutes rather than the time
of 10:30 A.M. or P.M.
Format
Times are expressed as a set of integers separated by colons (:).. Hours and minutes are required, but seconds are optional.
Within each field, leading zeros are ignored:
10:30
0:00
18:59
23:59:50
8:6:20
8:6:2
The minutes and seconds fields can contain values greater than 60. Values greater than 60 are automatically converted. For
instance 0:120:00 is the same as 2:00.
probe 00:120:00
2:00
Subseconds are specified using a decimal in the seconds field. Use either a period or a comma as the decimal point. The hours
and minutes fields become optional when the decimal is present. Subseconds are encoded to the nano-second, or one billionth of
a second:
probe 32:59:29.5
32:59:29.5
probe 1:10,25
0:01:10.25
probe 0:0.000000001
0:00:00.000000001
probe 0:325.2
0:05:25.2
Times can be followed by AM or PM , but no space is permitted. PM adds 12 hours to the time:
probe 10:20PM
22:20
probe 3:32:20AM
3:32:20
Times are output in a standard hours, minutes, seconds, and subseconds format, regardless of how they are entered:
probe 0:87363.21
24:16:03.21
Access
Time values have three refinements that can be used to return specific information about the value:
Refinement Description
/hour Gets the value's hour.
/minute Gets the value's minute.
lapsed-time: 91:32:12.14
probe lapsed-time/hour
91
probe lapsed-time/minute
32
probe lapsed-time/second
12.14
Times with time zones can only be used with the date! .
Creation
10:30
10:30
0:10:30
10:30:20.5
In the previous examples, the values are not evaluated. To evaluate values as mathematical expressions, use the reduce function:
10:35
In various math operations involving time values, the time values, integers, or decimals are converted as shown below:
probe 10:30 + 1
10:30:01
probe 10:00 - 10
9:59:50
probe 0:00 - 10
-0:00:10
probe 5:10 * 3
15:30
0:00:00.0015006
probe 8:40:20 / 4
2:10:05
0:00:20
Related
true
false
Use the now function with the /time refinement to return the current local date and time:
print now/time
14:42:15
If a value is a time! data type , wait delays for that period of time. If a value is a date!/time! , wait waits until the indicated
date and time. If the value is an integer! or decimal! , the function waits the indicated number of seconds. If the value is a port
, the function will wait for an event from that port. If a block is specified, it will wait for any of the times or ports to occur. It
returns the port that caused the wait to complete or returns none if the timeout occurred. For example,
probe now/time
14:42:16
wait 0:00:10
probe now/time
14:42:26
Tuple
Concept
It is common to represent version numbers, Internet addresses, and RGB color values as a sequence of three or four integers.
These types of numbers are called a tuple! (as in quin tuple ) and are represented as a set of integers separated by periods.
Format
Each integer field of a tuple! data type can range between 0 and 255. Negative integers produce an error.
Three to ten integers can be specified in a tuple. In the case where only two integers are given, there must be at least two
periods, otherwise the value is treated as a decimal.
1.2
decimal!
1.2.3
1.2.0
tuple!
Creation
Use the to-tuple function to convert data to the tuple! data type:
12.34.56
probe to-tuple [12 34 56]
12.34.56
Related
true
1.2.3.4
Use the mold function to convert a tuple into a string that can be read back into REBOL as a tuple:
1.2.3.4
Use the print function to print a tuple to standard output after using the reform function:
print 1.2.3.4
1.2.3.4
Words
Concept
Words are the symbols used by REBOL. A word may or may not be a variable, depending on how it is used. Words are often
used directly as symbols.
REBOL has no keywords, there are no restrictions on what words are used or how they are used. For instance, you can define
your own function called print and use it instead of the predefined function for printing values.
There are four different formats for using words, depending on the operation required.
Format
Words are composed of alphabetic characters, numbers, and any of the following characters:
? ! . ' + - * & | = _ ~
A word cannot begin with a number, and there are also some restrictions on words that could be interpreted as numbers. For
instance, -1 and +1 are numbers, not words.
The end of a word is marked by a space, a newline, or one of the following characters:
[ ] ( ) { } " : ; /
[test]
@ # $ % ^ ,
Words can be of any length, but cannot extend past the end of a line.
this-is-a-very-long-word-used-as-an-example
image-files l'image
++ -- == +-
***** *new-line*
left&right left|right
blue
Blue
BLUE
all refer to the same word. The case of the word is preserved when it is printed.
Words can be reused. The meaning of a word is dependent on its context, so words can be reused in different contexts. You can
reuse any word, even predefined REBOL words. For instance, the REBOL word if can be used in your code differently than
how it is used by the REBOL interpreter.
Creation
test
test:
:test
'test
Related
Use word? , set-word? , get-word? , and lit-word? to test the data type.
true
it is set
true
true
Copyright 2000 REBOL Technologies. All rights reserved.
WWW.REBOL.COM
REBOL/Core Users Guide
Appendix 2: Errors
Updated: 19-Jul-2001
Table of Contents
1. Overview
2. Error Categories
2.1. Syntax Errors
2.2. Script Errors
2.3. Math Errors
2.4. Access Errors
2.5. User Errors
2.6. Internal Errors
3. Catching Errors
4. Error Object
5. Generating Errors
6. Error Messages
6.1. Syntax Errors
6.1.1. invalid
6.1.2. missing
6.1.3. header
6.2. Script Errors
6.2.1. no-value
6.2.2. need-value
6.2.3. no-arg
6.2.4. expect-arg
6.2.5. expect-set
6.2.6. invalid-arg
6.2.7. invalid-op
6.2.8. no-op-arg
6.2.9. no-return
6.2.10. not-defined
6.2.11. no-refine
6.2.12. invalid-path
6.2.13. cannot-use
6.2.14. already-used
6.2.15. out-of-range
6.2.16. past-end
6.2.17. no-memory
6.2.18. wrong-denom
6.2.19. bad-press
6.2.20. bad-port-action
6.2.21. needs
6.2.22. locked-word
6.2.23. dup-vars
6.3. Access Errors
6.3.1. cannot-open
6.3.2. not-open
6.3.3. already-open
6.3.4. already-closed
6.3.5. invalid-spec
6.3.6. socket-open
6.3.7. no-connect
6.3.8. no-delete
6.3.9. no-rename
6.3.10. no-make-dir
6.3.11. timeout
6.3.12. new-level
6.3.13. security
6.3.14. invalid-path
6.4. Internal Errors
6.4.1. bad-path
6.4.2. not-here
6.4.3. stack-overflow
6.4.4. globals-full
1. Overview
Errors are exceptions that occur when certain irregular conditions occur. These conditions range from
syntax errors to file or network access errors. Here are a few examples:
12-30
1 / 0
** Math Error: Attempt to divide by zero.
** Where: 1 / 0
read %nofile.r
Errors are processed within the system as values of the error! datatype. An error is an object that, if
evaluated, will print an error message and halt. You can also catch errors and handle them in your
script. Errors can be passed to functions, returned from functions, and assigned to variables.
2. Error Categories
Syntax errors occur when a script uses REBOL syntax incorrectly. For instance, if a closing bracket is
missing or a string is missing its closing quote, a syntax error will occur. These errors only occur during
the load or evaluation of a file or string.
Script errors are general run-time errors. For instance, an invalid argument to a function will cause a
script error.
Math errors occur when a math operation cannot be processed. For instance, when attempting to
divide by zero an error will occur.
Access errors occur when a problem occurs with a file, port or network access. For example, an
access error will occur when attempting to read a file that does not exist.
2.5. User Errors
User errors are generated explicitly by a script by creating an error value and returning it.
3. Catching Errors
You can catch errors with the try function. The try function is similar to the do function. It evaluates a
block, but always returns a value, even when an error occurs.
When no error occurs, try returns the value of a block. For example:
10
the error is returned from the try and the print function cannot handle it.
To handle errors in a script, you must prevent REBOL from evaluating the error. You can prevent an
error from being evaluated by passing it to a function. For instance, the error? function will return true
when it is passed an error:
true
You can also print the data type of the value returned from a try:
The disarm function converts an error to an error object that can be examined. In the example below,
the error variable holds an error object:
When an error is disarmed, it will be an object! data type, not an error! datatype. Evaluating the
disarmed object will not cause an error:
make object! [
code: 400
type: 'math
id: 'zero-divide
arg1: none
arg2: none
arg3: none
near: [100 / 0]
where: none
]
Error values can be set to a word before they are disarmed. To set a word to an error, it must be
preceded by a function that prevents the error from propagating further. For example:
Setting a variable enables you to access the value of the block later. The example below will print an
error or non-error value:
4. Error Object
make object! [
code: 400
type: 'math
id: 'zero-divide
arg1: none
arg2: none
arg3: none
near: [100 / 0]
where: none
]
code The error code number. These are obsolete and should not be used.
type The type field identifies the error category. It is always a word data type
of syntax, script, math, access, user, and internal.
id The id field is the name for the error as a word. It identifies the specific
error that occurred within the error category.
arg1 This field holds the first argument to the error message. For instance, it
may include the data type of the value that caused the error.
arg2 This field holds the second argument to the error message.
arg3 This field holds the third argument to the error message.
near The near field is a code fragment that shows where the error occurred.
You can write code that checks any of the error object fields. In this example, the error is printed only
when the error id indicates a divide by zero error:
The error id word also provides the error block that will be printed by the interpreter. For example:
5. Generating Errors
User errors can be generated. The simplest way to generate an error is make it. Here is an example:
Any of the existing errors can be generated by making the error with a block argument. This block
contains the error category name and the error message id name. If the error requires arguments, the
arguments follow the message id name. The arguments are what define the arg1, arg2 and arg3
values in the error object. Here is an example:
Custom errors can be entered into the system/error object's user category. This is done by making a
new user category with new entries. These entries are used when generating errors. For instance, the
following example enters an error into the user category:
make object! [
code: 803
type: 'user
id: 'my-error
arg1: none
arg2: none
arg3: none
near: [make error! [user my-error]]
where: none
]
To create more informative errors, define an error that uses data available when it is generated. This
data is included in the disarmed error object and printed as part of the error message. For instance, to
use all three argument spaces in an error object:
make object! [
code: 803
type: 'user
id: 'my-error
arg1: [this]
arg2: "that"
arg3: 'my-function
near: [make error! [user my-error [this] "that" my-function]]
where: none
]
The error message generated for my-error can be printed without stopping the script:
A new library category may be created if there is a need to group a series of errors together by making
a new category in system/error:
The type defined in the error object will be the error type printed when the error is generated. The
following example illustrates generating an error from both error1 and error2 in the my-error category.
a simple error
Finally, the description that returns the errors defined in my-errors may be obtained with:
My Error Category
6. Error Messages
Listed below is a list of all errors defined in the system/error object error catalog.
6.1.1. invalid
Data could not be translated into a valid REBOL datatype. In other words, a malformed value was
evaluated.
Message:
Example:
6.1.2. missing
Message:
6.1.3. header
An attempt was made to evaluate a file as a REBOL script and the file did not have a REBOL header.
Message:
Example:
6.2.1. no-value
Message:
Example:
An attempt was made to define a word to nothing. A set-word was used without an argument.
Message:
Example:
6.2.3. no-arg
A function was evaluated without providing it with all the arguments it was expecting.
Message:
Example:
f: func [b][probe b]
filter-error try [f]
6.2.4. expect-arg
Message:
Example:
f: func [b [block!]][probe b]
filter-error try [f "string"]
** Script Error: f expected b argument of type: block
** Where: f "string"
6.2.5. expect-set
Two series values were used together in a way that was not compatible. For instance, when trying to
do a union between a string and a block.
Message:
Example:
6.2.6. invalid-arg
This is a generic error for handling values that were used improperly. For instance, when a set-word is
used inside of a function's specification block.
Message:
Example:
6.2.7. invalid-op
An attempt was made to use an operator that had been redefined. The operator used is no longer a
valid operator.
Message:
6.2.8. no-op-arg
A math or comparison operator was used without providing the second argument.
Message:
Example:
filter-error try [1 +]
6.2.9. no-return
A function expecting a block to return a value did not return anything. For instance, when using the
while or until function.
Message:
Examples:
10
** Script Error: Block did not return a value
** Where: while [print 10] [probe "ten"]
filter-error try [
until [print 10] ; block returns nothing
]
10
** Script Error: Block did not return a value
** Where: until [print 10]
6.2.10. not-defined
Message:
6.2.11. no-refine
An attempt was made to use a function refinement that didn't exist for that function.
Message:
Example:
6.2.12. invalid-path
An attempt was made to access a block or object value using a path that did not exist within that block
or object.
Message:
Example:
6.2.13. cannot-use
An attempt was made to perform an operation on a value of an incompatible datatype. For instance,
when attempting to add a string to a number.
Message:
Example:
6.2.14. already-used
An attempt was made to alias a word that had already been aliased.
Message:
Example:
Message:
Example:
blk: [1 2 3]
filter-error try [poke blk 5 "five"]
6.2.16. past-end
An attempt was made to access series data beyond the length of the series.
Message:
Example:
blk: [1 2 3]
filter-error try [print fourth blk]
6.2.17. no-memory
Message:
6.2.18. wrong-denom
A math operation was performed on money values of two different denominations. For instance, when
trying to add USD$1.00 to DEN$1.50.
Message:
Example:
6.2.19. bad-press
An attempt was made to decompress a binary value that was corrupt or not a compressed format.
Message:
Example:
6.2.20. bad-port-action
An attempt was made to perform an unsupported action on a port. For instance, when trying to use
find on a TCP port.
Message:
6.2.21. needs
An attempt was made to run a script that needed either a new version of REBOL or a file that couldn't
be found. This information will be found in the script's REBOL header.
Message:
6.2.22. locked-word
An attempt was made to modify a protected word. The word will have been protected with the protect
function.
Message:
Example:
my-word: "data"
protect 'my-word
filter-error try [my-word: "new data"]
6.2.23. dup-vars
A function was evaluated that had multiple occurrences of a word defined in its specification block. For
instance, if the word arg was defined as both argument one and two.
Message:
Example:
A file could not be accessed. This could be a local or network file. Most common reason for this error is
a nonexistent directory.
Message:
Example:
6.3.2. not-open
Message:
Example:
p: open %file.txt
close p
filter-error try [copy p]
6.3.3. already-open
Message:
Example:
p: open %file.txt
filter-error try [open p]
6.3.4. already-closed
An attempt was made to close a port that had already been closed.
Message:
Example:
p: open %file.txt
close p
filter-error try [close p]
6.3.5. invalid-spec
An attempt was made to create a port with make using a specification that a port could not be built
from.
Message:
Example:
6.3.6. socket-open
6.3.7. no-connect
A connection to another host failed. This is generic error covering a range of reasons for the
connection failure. When more information is known about the reason for the connection failure, a
more specific error will be thrown.
Message:
Example:
6.3.8. no-delete
An attempt was made to delete a file that was either locked or protected.
Message:
Example:
p: open %file.txt
filter-error try [delete %file.txt]
6.3.9. no-rename
An attempt was made to rename a file that was either locked or protected.
Message:
["Cannot rename" :arg1]
Example:
p: open %file.txt
filter-error try [rename %file.txt %new-name.txt]
6.3.10. no-make-dir
An attempt was made to create a directory in a file path that did not exist or was write protected.
Message:
Example:
6.3.11. timeout
The timeout period elapsed while waiting to for a response from another host. This timeout is set in the
port's timeout attribute.
Message:
Network timeout
6.3.12. new-level
An attempt was made within a script to change the security to a lower level of security that was denied.
This is to say, whenever a script requests a lower security setting and the user denies the request, this
error is thrown.
Message:
["Attempt to change security level to" :arg1]
Example:
secure quit
filter-error try [secure none] ; denied request
secure none
6.3.13. security
A security violation occurred. This will happen when an attempt is made to access a file or the network
when the secure setting is set to throw.
Message:
Example:
secure throw
filter-error try [open %file.txt]
secure none
6.3.14. invalid-path
Message:
Example:
Message:
Example:
6.4.2. not-here
Message:
6.4.3. stack-overflow
Message:
["Stack overflow"]
Example:
6.4.4. globals-full
The maximum allowable number of defined global words has been exceeded.
Message:
Appendix 3: Console
Updated: 18-Jul-2001
Table of Contents
1. Command Prompt
2. Result Indicator
3. History Recall
4. Busy Indicator
5. Advanced Console Operations
5.1. Keyboard Input Sequences
5.2. Terminal Output Sequences
1. Command Prompt
The default command line prompt is ">>". You can change the prompt with code such as:
Input:
The prompt can be a block that is evaluated each time. This line prints the current time:
10:30 >>
2. Result Indicator
The default result indicator is "==" and can be modified with a line such as:
These settings can be placed in the user.r file to make them permanent.
3. History Recall
Each line typed into REBOL at the prompt is stored in a history block, and it can be recalled
later using the up and down arrow keys. For instance, pressing the up arrow once recalls the
prior input line.
The history block containing all input lines is accessed from the system console object:
probe system/console/history
These lines can be put in the user.r file to save and reload your history between REBOL
sessions.
4. Busy Indicator
When REBOL waits for a network operation to complete, a busy indicator appears on screen
to indicate that something is happening. You can change the indicator with a line like:
system/console/busy: "123456789-"
Whe REBOL is running in quiet mode, te busy indicator will not be displayed.
The console control sequences follow the ANSI standard. These features provide you with
the capability to write your own platform-independent terminal programs such as text editors,
email clients, or telnet emulators.
The console features apply to both input and output. On input, function keys will be converted
to multiple-character escape sequences. On output, multiple-character escape sequences
can be used to control the display of text in the console window. Both the input and output
sequences begin with the ANSI escape character, 27 decimal (1B hex). The next character in
the sequence indicates the control keys on input or the terminal control operation on output.
The ANSI control characters are case-sensitive and normally require an upper case
character.
The special keys and second character in the sequence are included in the following table:
There are several variations in the terminal control output character sequences. Some
command codes are preceded by a number (sent in ASCII) indicating that the operation is to
be performed the specified number of times. For example the cursor motion command may
be preceded by two numbers separated by a semicolon to indicate the row and column
position to move to. The cursor command characters (upper case required) are included in
the following table:
The following example moves the cursor to the right ten spaces:
print "^(1B)[10CHi!"
Hi
This example moves the cursor to the left seven spaces and clears the remainder of the line:
How a
To find the current console window size, you can use this example:
33;105R
close cons
The above example opens the console, sends a control character to the input buffer and
copies the return value. It reads the value (screen dimensions) that is returned after the
control character and closes the console. The return value is the height and width separated
by a semicolon (;) and followed by an R. In the above example, the screen is 33 high by 105
wide.
Autoscroll