The Isis Primer
This introduction to Isis assumes you have a basic understanding of
computer programming and a working knowledge of the Unix operating
system. If you are completely new to programming or Unix, you may
wish begin elsewhere. Contact Stefan for some suggestions on how best
to proceed.
Running Isis
In general, you can always start an Isis interpreter by simply typing
isis at your shell prompt. You will see something like this:
$ isis
*** Isis 6
->
The ->
is Isis's prompt, letting you know that it is
ready for you to type expressions directly into the interpreter.
Alternatively, you can write an Isis program using your favorite text
editor and run Isis on the contents of a file by passing the name of
the file as an argument, like this:
$ isis test1.isis
...
Any of the examples shown below can be written in a file or typed
directly into the Isis interpreter with exactly the same effect.
Check the getting
started section for more information on how to install Isis and
how to work with Isis interpreters under Unix-based operating systems.
Expressions and Values
The Isis interpreter reads expressions from your keyboard or
from a file, and evaluates each one to produce a value.
If you type the expression directly on your keyboard, its value will
be printed on your screen for you to see. You can enter several
expressions on a single line if you like, and they will be evaluated
in order.
The simplest kind of expression you could possibly enter is a
constant. For a very simple example, type the number 42 in the
interpreter and press return:
-> 42
42
->
In this case, you typed the expression 42, which
evaluated to the value 42. All constants evaluate to
themselves.
Types
Every value in Isis has a type. There are 7 types
possible in Isis: integer, real number, character, boolean, address,
procedure, and list. The first six of these are called basic
types because they cannot be subdivided further. Lists are merely
ordered collections of values of any type. Each of these types is
explained below:
- Integer constants are expressed by entering a number
without a decimal point, and they are printed in the same way.
The range of possible values you can enter is dictated by your machine
type.
-> 42
42
-> -80
-80
-> 0
0
An integer constant can also be expressed in binary form by using the
prefix 0b or 0B, like so:
-> 0b1111
15
-> 0b10001000
136
-> 0b11111111
255
- Real number constants must be entered with a decimal point or
using exponential notation (the lowercase letter 'e' followed
by an exponent). Again, range is determined by your machine's
floating point range.
-> 42.0
42.0
-> -32.5
-32.5
-> 0.0
0.0
-> 6.63e-34
6.63e-34
-> 6.02e23
6.02e+23
-> 3e8
300000000.0
- Characters are expressed by entering a character enclosed in
single quotes, and they are printed in the same way.
-> 'r'
'r'
-> '2'
'2'
-> '?'
'?'
There are also special character constants corresponding to control
characters that are entered with a backslash preceding the character.
Each of these special characters is also bound to a pre-defined
variable. You can use either the literal constant or the variable to
refer to these characters:
Literal | Preset variable |
'\a' | alert |
'\b' | backspace |
'\f' | formfeed |
'\n' | newline |
'\r' | return |
'\t' | tab |
'\v' | vtab |
'\'' | squote |
'\"' | dquote |
'\\' | backslash |
- Booleans have only 2 possible values, and they are entered and
printed simply as True and False.
-> True
True
-> False
False
- Addresses represent a location in the computer's memory. You can
specify an address constant by entering a hexidecimal number preceded
by the prefix 0x. They are printed in the same way.
Addresses are commonly only used as return values from and arguments
to external C functions. For this reason, you will probably never
need to enter an address constant or modify addresses that are
returned to you. But you have the ability nevertheless:
-> 0xFFFFFFFFFF
0xffffffffff
-> 0x28478ddd
0x28478ddd
-> 0x0
0x0
- Procedures represent an action that should be performed on a set
of values that has yet to be determined (the arguments). They are
created using the proc construct described later. For
brevity's sake, procedures are never actually printed on the
screen--only an indication that a value is a procedure. A different
indication is given if the procedure links to an external C function.
However, from the interpreter's point of view, these two kinds of
procedures are exactly equivalent.
-> (proc (x) x)
Proc
-> head-insert
Cfunc
- Lists are formed by enclosing any number of expressions in square
brackets. Any expression is valid--from the constant expressions
described above to the more complex constructs described in the next
section. Lists may nested as well. More information about lists is
coming later, but here are a few examples to get you started:
-> [ 3 4 5 ]
[ 3 4 5 ]
-> [ 42.42 'q' True (proc (x) (+ x 42)) ]
[ 42.420000 'q' True Proc ]
-> []
[]
-> [ [3 2 1] [2 1] ['x'] [] ]
[ [ 3 2 1 ] [ 2 1 ] "x" [] ]
Comments
Like most other programming languages, you can embed comments in your
Isis programs that will be ignored by the interpreter. In Isis, any
text entered following the character # on a single line
will be treated as a comment. Placing comments in strategic places
will greatly enhance the readability of your programs.
-> 43 # This is a comment that will be ignored by the interpreter
43
-> 'x' ##### hi mom
'x'
You can create multi-line comments by following the #
with a curly-bracket {. Everything up to the next
closing curly-bracket } will be considered a comment.
-> [ 1 2 3 #{ This is
a multi-line
comment } 4 5 6 ]
[ 1 2 3 4 5 6 ]
Multi-line expressions
If the expression you are typing is too large to fit on one line, you
can press return in the middle of the expression as many times as you
like. Isis will only evaluate the expression when all brackets and
parentheses have been closed.
-> [1 2 3 4 5 6 7 8 9
10 11 12 13 # here is a frivolous comment
14 15 16 17 18]
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ]
->
Strings
A "string" is computer jargon for a sequence of characters that
represents a quotation or other piece of text. In Isis, strings are
handled simply as lists that contain only character values. However,
it is tiresome to type out each character constant in a list in order
to create a string. Thankfully, Isis provides a short-cut---all you
have to do is enclose the sequence of characters in double quotes.
Typing out the list of character literals will produce exactly the
same result. In addition, when printing a list, the interpreter
checks if all the elements of the list are characters, and if so, a
string enclosed in double quotes is printed instead (just as you might
have entered it).
-> "hello!"
"hello!"
-> [ 'h' 'e' 'l' 'l' 'o' '!' ]
"hello!"
Null
Isis provides the ability to express an "empty" (or "null") value by
entering Null. Null values represent the absence or
nonexistence of a value. Null is commonly returned from
procedures when a return value does not make sense or if there is an
error. Null evaluates to itself, and it is printed simply as
Null.
-> Null
Null
Errors
The interpreter will print error messages whenever it can't understand
what you have typed in. By convention, errors will always appear on
lines by themselves preceded by an asterisk *.
-> 'qq'
* Error line 1: Bad character constant.
'q'
-> ]
* Error line 2: Too many close parens/brackets.
Null
-> Falsse
* Variable 'Falsse' has not been defined.
Null
->
Isis provides a number of debugging utilities that are described in
the debugging section of the
documentation that you might like to read later.
Other Isis expressions
The following sections describe other core language expressions
available in Isis. In each definition, any italicized word may
be replaced by any expression described in this document, including
the constant expressions described above. Any elements surrounded in
curly brackets { } are optional. Elipses
(...) indicate that an arbitrary number of elements following
the established pattern is allowed. Finally, varname
represents a variable name, which must be a sequence of characters
that does not begin with a number, a period, or the characters
+ or -.
Variables
Variable binding
( set varname exp )
Variable reference
varname
Variable query
( bound? varname )
Variables are named storage locations for values of any type. Use the
set expression to bind a variable to the value of a
particular expression. If the variable was already bound, its value
will be reset to the new value. To retrieve the value of a variable,
simply enter the variable name. Use bound? to check if a
variable is defined in the current environment. bound? will
return either True or False.
-> (set x 42)
42
-> (set y "abc")
"abc"
-> x
42
-> y
"abc"
-> (set x [ 1 2 3 ])
[ 1 2 3 ]
-> x
[ 1 2 3 ]
-> (bound? x)
True
-> (bound? q)
False
Lists
List formation
[ exp exp ... ]
List reference
( list index )
A list is simply an ordered collection of values of any type. To
create a list, simply enclose an arbitrary number of expressions in
sqaure brackets [ ]. The expressions are evaluated
and their values become the elements of the list.
The list reference expression retrieves a particular item in a list.
list must evaluate to a list value and index must
evaluate to an integer. The first item in a list is numbered 0
(zero). Null will be returned and an error will be printed
if the index is out of range.
-> (set alist [10 11 12 13 14 15])
[ 10 11 12 13 14 15 ]
-> (alist 3)
13
-> (alist 10)
* Index 10 out of range.
Null
-> (set blist [ ["stefan" "E15-348"] ["vmb" "E15-324"] ["willy" "E15-339"]])
[ [ "stefan" "E15-348" ] [ "vmb" "E15-324" ] [ "willy" "E15-339" ] ]
-> ((blist 2) 1)
"E15-339"
Memory manipulation
Memory retrieval
( address memtype )
( address [ memtype len ] )
( address [ memtype len offset ] )
Memory storage
( address memtype val )
( address [ memtype len ] val )
( address [ memtype len offset ] val )
These expressions allow you to access and manipulate data directly in
memory. Single elements or arrays of data may be stored and retrieved
from any given address, with an optional offset. These operators are
explained on the memory
manipulation documentation page, along with other useful memory
utilities, including routines for allocating and deallocating memory
for your own use.
Conditional evaluation
Conditionals enable you to branch the execution of your program in
different ways depending on the value of a certain expression. Isis
has 4 such constructs, each described below. Typically, Isis will
evaluate a condition expression, and depending on whether its value
logically true or logically false, the interpreter will
proceed in a different way, leaving certain expressions unevaluated.
Isis considers a value to be logically true if it is a non-zero
number, character, or address, or if the value is a procedure or a
non-empty list, or if it is the boolean True. Any other
value, including Null, is considered logically false.
if statement
( if exp then { else } )
If exp evaluates to a logically true value, the result of
evaluating the then expression is returned. Otherwise, the
result of the else expression, if it exists, is returned. If
the else expression is omitted, Null is returned.
Only one of either the then or else expressions is
evaluated.
(print "Please enter username: ")
(set username (read-string))
(if (= username "Stefan")
(print "Welcome Sir." newline)
(print "Access denied." newline))
cond statement
( cond ( exp val ) ( exp val ) ... )
The exp expressions are evaluated in order until one of them
results in a logically true value, and then the result of the
corresponding val expression is returned. Only the val
expression corresponding to the first logically true condition is
evaluated. A condition of True could be used at the end to
specify a default action. If none of the conditions results in a
logically true value, Null is returned.
(print "Please enter your age: " newline)
(set age (eval (read-string)))
(set bev (cond ((< age 2) "warm milk")
((< age 12) "fruit punch")
((< age 21) "a milkshake")
((> age 50) "a glass of lemonade")
(True "a shot of tequilla")))
(print "Your beverage will be " bev ". Please be seated." newline)
while statement
( while exp body )
The body expression is evaluated repeatedly as long as
exp results in a logically true value. The value of the entire
while expression is the value of the body the last
time it is evaluated, or Null if it is never evaluated
because the condition was initially false.
(set x 10)
(while (> x 0) (begin (print x) (sleep 1) (set x (- x 1))))
(print "Blastoff!" newline)
switch statement
( switch exp ( case val ) ( case val ) ... { ( else val ) } )
exp is evaluated, and its value is compared in sequence to the
results of the case expressions until a match is found, and the
result of the corresponding val expression is returned. If
desired, use the keyword else at the end to specify a default
action. If no match is found and no else is present,
Null is returned.
(print "Please enter your preference: ")
(set selection (read-string))
(set dinner (switch selection
("carnivore" "barbequed ribs")
("vegetarian" "a spinach omelette")
("vegan" "cucumber salad")
(else "freshly cut twigs")))
(print "Your dinner will be " dinner ". Please be seated." newline)
Logic
Isis provides the logical operators and, or,
nand, and nor:
( and exp exp ... )
( or exp exp ... )
( nand exp exp ... )
( nor exp exp ... )
The and statement returns True only if all its
expressions evaluate to logically true values. The or
statement returns True if just one or more of its expressions
evaluate to logically true values. Otherwise, these
expressions return False. nand and nor are
analogous to and and or except they will return the
opposite value ("not" and and "not" or).
As stated above, Isis considers a value to be logically true if
it is a non-zero number, character, or address, or if the value is a
procedure or a non-empty list, or if it is the boolean True.
Any other value, including Null, is considered logically
false.
In these constructs, Isis only evaluates the necessary expressions
from left to right to determine the result. For example, if the
first expression in an and statement evaluates to a false
value, any remaining expressions are not evaluated, because their
values would have no effect on the final result.
(print "Please enter your hair color: ")
(set hair (read-string))
(print "Please enter your sex: ")
(set sex (read-string))
(print "Please enter your shoe size: ")
(set shoe (eval (read-string)))
(cond ((and (= hair "brown") (= sex "male") (= shoe 10))
(print "Hi Wilbert." newline))
((and (= sex "female") (= hair "blonde"))
(print "Please to meet you, maam." newline))
((or (= hair "green") (> shoe 20) (and (!= sex "female") (!= sex "male")))
(print "Welcome to Earth." newline))
(True
(print "Your information has been recorded. Goodbye." newline)))
Sequential evaluation
begin statement
( begin exp exp ... )
The begin statement allows you to encapsulate several
operations in a single expression. Each expression is evaluated in
order, and the value of the final expression is returned.
(print "Please enter your current heart rate: ")
(set rate (eval (read-string)))
(set period (* 60.0 (/ 1.0 rate)))
(print "I will now count your heartbeats: " newline)
(set x 1)
(while True
(begin (print x)
(sleep period)
(set x (+ x 1))))
Local environments
local statement
( local ( varname varname ... ) exp )
The local expression sets up a local environment
consisting of the specified variables. A local environment is a
collection of variables that are accessible only within the body of
the local statement. These variables are automatically given
an initial value of Null.
The given expression is then evaluated in the context of this new
environment and its value returned. Since the local variable names
may duplicate those in the parent environment, variable references in
the body expression are resolved by first looking in the local
environment and then, if not found there, in the parent environment.
(set x 3)
(set y 7)
(set z 32)
(local (y z)
(begin
(set y 100)
(set z 200)
(print "Inside the local statement: " (+ x y z) newline)))
(print "Outside the local statement: " (+ x y z) newline)
Procedures
Procedure definition
( proc ( varname varname ... ) exp )
( proc varname exp )
Procedure application
( proc exp exp )
Procedures express an action that should be taken on a set of values
that has not yet been determined (the arguments). Procedures may be
defined in two ways. In the first way, specific variable names are
given (enclosed in parentheses) for each argument that will be passed
to the procedure when it is applied. A procedure of this kind must
always be called with exactly that number of arguments. In the second
way, a single variable name is given that will become bound to a
list of the arguments passed to the procedure. This allows the
procedure to accept an arbitrary number of arguments.
When a procedure is defined, it includes a reference to the
environment within which it was defined. Then, when the procedure is
applied, a new local environment is created on top of this environment
in which the procedure's argument variable names are bound to the
corresponding values that are passed to it. The given expression will
be evaluated in the context of this new environment. Therefore,
variable references in the body are resolved first in the local
environment (the argument variables) and then in the parent
environment (the environment in which the procedure was
defined, not the environment in which it is applied).
The result of a procedure definition is a procedure value. You
can invoke the procedure by using this value in a procedure
application expression. In a procedure application, the proc
expression must evaluate to a procedure value. This procedure is
applied to the values of the remaining expressions. Procedure
application is the same regardless of whether the procedure was
written directly in Isis or if it links to an external C function.
-> (set add3 (proc (fred) (+ fred 3)))
Proc
-> (add3 10)
13
-> (add3 20 30)
* Incorrect number of arguments in call (2 for 1).
23
-> (set dup (proc arglist (append arglist arglist)))
Proc
-> (dup 1 2 3 4)
[ 1 2 3 4 1 2 3 4 ]
-> (set factorial (proc (x) (if (= 0 x) 1 (* x (factorial (- x 1))))))
Proc
-> (factorial 10)
3628800