SLOBIL
Table of Contents
- 1. Introduction
- 2. The Language
- 3. Example Programs
- 4. Example Instructions
- 5. Reference
- 6. Saved Objects
- 7. GNU Free Documentation License
1. Introduction
SLOBIL is easy to use interactively. It is natural to write line-by-line in the REPL and has a command-like syntax. SLOBIL programs can just be collections of functions executed in the SLOBIL REPL itself because it is a natural command syntax.
SLOBIL is a dynamically-typed language. You do not define new types in the language. Instead the language provides a few flexible types. The language has a prototype object system, i.e. reference instances of an object are used to create new instances and changes to parent instances cascade down the chain. In other words, you do not declare an object a member of class X. You say, this object is like that other object, except in these ways.
SLOBIL prioritizes what the caller of the code wants to do. Instructions, which would be functions or procedures in other languages, do not have fixed argument lists and users can modify other aspects of the instruction that, perhaps, the original author did not intend. The language gives you the sharp knife and lets you cut.
It is biased against strict encapsulation and towards flexibility for the end user.
It has superficial syntax similarities to Tcl in that the language is a command-based language that uses different types of parenthesis to do different things. Less superficially, similarly to Tcl, it lets everything be overwritten (with different semantics) and no keywords are reserved. The typing is much stronger than Tcl though, and the semantics of the two languages are different. The object system is prototype-based instead of the class-based system in Tcl.
2. The Language
Lexical Elements
Slots are represented by a forward-slash (/
) followed by any non-whitespace characters. Slot names are case-sensitive. /a
and /A
refer to different slots. I use lower case names for slots with multiple words separated by hyphens. Like this: /a-name-for-a-slot
. But the language allows for alternative naming conventions.
A literal number is a sequence of digits ([0-9]) that includes at most one decimal point represented by a period. It may also be preceded by a hyphen to indicate a negative number. A literal number contains no spaces.
A literal string is a sequence of characters where the first and last characters are double quotation marks. A literal string can contain the following escape sequences preceded by a backwards slash \
.
\
outputs\
.t
outputs tabn
outputs newliner
outputs carriage return'
outputs a single quote character
A literal string outputs a double quotation mark when a single quotation mark is used '
. A literal string can contain whitespace, including newlines.
A literal boolean value is a string of characters that is either True
or False
.
A literal nothing value is a string of characters that is Nothing
.
A single apostrophe ('
) is a token that starts a comment which is ended by a new line.
The following characters are special tokens: [, (, {, ., }, ), ]
.
If a sequence of characters without any whitespace is (1) not a literal string, (2) not a literal number, (3) not a special token, (4) not a comment, (5) not a literal boolean, (6) not a literal nothing, and (7) does not begin with a forward slash, then it is considered a data reference. Some names that are valid for slots are not valid for data references. Data references refer to the data currently inserted in the slot with the same name.
SLOBIL programs are whitespaced-separated lists of the above tokens.
Data and Types
Programmers do not define types. The following are the data types: Integer, Real, String, Slot, Object, Instruction, Expression, Operation, File, Boolean, Nothing, and Task.
Integer's are numbers without decimal points. They are "big" and can be arbitrarily large in magnitude.
Real's are numbers with decimal points. They are double precision floating-points.
String's are UTF-8 encoded character strings. Internally, they are represented as UTF-32 strings for speed, but they output to UTF-8.
Slot's indicate the location of data and also can act as symbols. They are written by giving a name that begins with a forward-slash.
Object's can contain any number of slots and store data in these locations. Code is executed inside of objects and can refer to data located in the object's slots. Objects are represented internally as a hash table.
Instruction's are SLOBIL code objects that are executed only when the programmer explicitly requests their execution. They are the primary way programmers write functions, procedures, and methods. Instructions are created by enclosing code between the tokens (
and )
.
Expression's are SLOBIL code objects that are executed whenever the programmer requests their value, i.e. they are lazy. Expressions are created by enclosing code between the tokens {
and }
.
Operation's are code objects that take a series of positional arguments. The built-in operations are written in C, but users can also define operations.
File's are stream objects used for writing to external files.
Boolean's are either True or False.
Nothing is the type for the literal nothing value Nothing. Nothing else has this type.
Task's are processes running on the computer. When the program starts, it is in the main task. Additional tasks can be created for multi-threaded programs.
File, Task, and Object data are passed by reference. All others are by value.
Statements and Objects
A statement is a list of whitespace-separated tokens ended by a .
. Naturally, the tokens included in the list cannot include .
.
... TOKEN TOKEN ... TOKEN .
Statements happen inside objects. The object a statement is executed in determines what data is returned by a data reference. Data references look for a value in the relevant slot in the current object, if none is found, then they look for an object in the slot in the object containing the current object and so on.
Statements are executed effectively inside temporary objects. Arguments in a statement generally determine the values located at slots in the temporary object.
Statements begin with an operation. There are a few exceptions to this rule where the operation is automatically-selected based on the data type used for the first argument. This is for convenience. The entire language can be accessed with statements that begin with an operation.
The obligatory first statement in SLOBIL:
... print "Hello, world!" .
Creating Objects
Objects are created by using the object
operation, followed by an alternating sequence of slots and data elements.
... object /number 10 /string "Hello!" /real 3.2 . ans = an Object with: number of type Integer, value: 10 string of type String, value: "Hello!" real of type Real, value: 3.200000
The object
operation sets the /ans
slot to the relevant object.
The /ans
Slot
Each Object has a special slot called the /ans
slot. Many built-in operations put data in the /ans
slot. It is the standard place to return values from an instruction, operation, or expression. For example,
... add 4 5 . ans = 9 ... print ans . 9
Substatements
Substatements are statements included as an element of another statement. They are surrounded by square brackets ([]
) and executed in the order they appear in the statement prior to the statement itself being evaluated. When the outer statement is evaluated the substatements are replaced by the value each substatement set the /ans
slot to after it was executed.
For example,
... object /number 10 /string "Hello!" /real 3.2 /object [ object /x 3 /y 2 ] . ans = an Object with: number of type Integer, value: 10 string of type String, value: "Hello!" real of type Real, value: 3.200000 object of type Object, value: an Object with: x of type Integer, value: 3 y of type Integer, value: 2 ... add 5 [ add 4 3 ] . ans = 12
Substatements do not need to include a .
to end the last statement in the substatement. They can, but they do not have to. Substatements can include multiple statements.
The brackets can be placed right next to the other tokens. For example,
... add 5 [add 4 3] . ans = 12
Will work, but the thing to keep in mind is that a sentence is not complete with a period until some whitespace follows the period. So this will not work.
... ' Will not work ... add 5 [add 4 3 .] ... 'Will work ... add 5 [add 4 3 . ]
So if you prefer putting parenthesis next to the other tokens, I recommend not including the .
on the last statement.
I prefer always including spaces between the parenthesis and other tokens.
Inserting Data Into Slots
Slots determine the location of data. The set
operation inserts data into a slot, like so:
... set /number 10 . ... print number . 10
The set
operation takes the slot to insert to and the data to insert as arguments.
The second line in the above example, print number .
, demonstrates how to access the data located at a slot. Simply omit the forward slash to get the data.
Because this is a common procedure in SLOBIL, if the first element of a statement is a slot, then SLOBIL assumes you want to call set
. For example, the above could have been written as:
... /number 10 .
set
has an optional third argument: the object whose slot we should insert the data into. If the third argument is omitted, then the value is inserted into the current object's slot.
... /rectangle [ object /length 10 /width 5 ] . ... print rectangle . an Object with: length of type Integer, value: 10 width of type Integer, value: 5 ... /width 4 rectangle . ... print rectangle . an Object with: length of type Integer, value: 10 width of type Integer, value: 4
The move
operation moves data between different slots without copying the data.
... /x "Hello." . ... print x . Hello. ... move /x /y . ... print y . Hello. ... print x . Error: Value at slot /x not found.
The delete
operation removes data from the slot.
... /x "Hello." . ... print x . Hello ... delete /x . ... print x . Error: Value at slot /x not found.
Instructions
Instructions are a data type that contains code to execute. They are created by enclosing a series of statements in parenthesis. The last statement in the parenthesis does not have to end with a period.
For example:
... /add-one ( add t 1 ) . ... print add-one . ( add t 1 )
The call
operation calls an instruction in a temporary object determined by alternating slots and data values similar to the call to an object. In effect, instructions are called with named arguments.
... call add-one /t 4 . ans = 5
When instructions execute, whatever data they set the /ans
slot to in the temporary object in which they execute is sent back to the object from which they are called. No other slots they assign are modified in the current object.
... /circle-area ( /pi 3.1459 . /r2 [ power r 2 ] . mul r2 pi . ) . ... call circle-area /r 2 ans = 12.583600 ... print ans. 12.583600 ... print pi . Error: Value at slot /pi not found. -> print pi .
Calling instructions is a common thing to do. So, if the first element of a statement is an instruction, SLOBIL assumes the programmer wants to call
it.
... circle-area /r 2 . ans = 12.583600
Instructions are just code, they do not carry an environment around with them. When the programmer call
's an instruction, the programmer determines the environment the code is executed in. Instructions do not have a fixed set of arguments but allow the caller of the code to manipulate the environment in which they are called arbitrarily. For example:
... /add-one ( add t 1 ) . ... add-one /t 5 . ans = 6 ... add-one /t 5 /add sub . ans = 4
Any non-literal in an instruction can be changed by adjusting the environment in which the code is executed.
To bind an instruction to an environment, you can use the bind
operation. For example,
... /add2 ( add t s ) . ... bind add2 /t 3 . ... add2 /s 4 . ans = 7
Getting data
The get
operation sets the /ans
slot to the data located at a certain slot.
... /x "Hello." . ... /y [ object /test "Goodbye." ] . ... get /x . ans = "Hello." ... get /test . ... get /test y . ans = "Goodbye."
The second argument to get
is the object to get the data from. If it is omitted, the data is grabbed from the current object. There is also a shorthand notation for getting data from an object. The notation object:slot
returns the data located at the slot within the object. The notation can also be nested like object:slot1:slot2
where another object is located at object:slot1
.
... print y:/test . Goodbye
One limitation to the colon notation is that it cannot be used with substatements.
... ' Will give an error ... print y:[ answer /test ] .
The exist
operation tests whether data exists at a given slot in an object. Like get
, it has an optional second argument to give the object. If the argument is omitted, it searches the current object. It sets the /ans
slot to True
if data exists at the slot and False
if the data does not.
... exist /new-slot . ans = False. ... /new-slot 0 . ... exist /new-slot . ans = True.
Control flow operations
The if
operation sets the /ans
slot to its second argument if its first argument is True
and to its third argument if its first argument is False
.
... if True "Hello." "Goodbye." . ans = "Hello." ... if False "Hello." "Goodbye." . ans = "Goodbye."
The if
operation can be used for control flow by using Expressions. Expressions are created by enclosing statements in {}
. The code in an expression is executed when its value is accessed.
... /x 10 . ... if [ gt x 3 ] { print "x is greater than 3." } { print "x is less than three." } . x is greater than 3. ... /x 1 . ... if [ gt x 3 ] { print "x is greater than 3." } { print "x is less than three." } . x is less than three.
Expressions are like substatements in that they evaluate to what they set the /ans
slot to. Where they differ is that substatements are executed before the main statement is evaluated. Expressions are executed only when they are needed. Expressions are necessary for the if
operation to function as a control flow operation. If we wrote the above statements using substatements, all the statements would be evaluated regardless of the truth of the first argument.
... if [ gt x 3 ] [ print "x is greater than 3." . answer 0 . ] [ print "x is less than three." . answer 0 . ] . x is greater than 3. x is less than three. ans = 0
The answer
operation sets the /ans
slot to a value. To demonstrate the above, I had to add it because otherwise the second and third argument to if
would appear to be missing to the code because substatements evaluate to what they set the /ans
slot to before the statement processes.
The while
operation is a looping operation. It takes two instructions for its argument. It calls the first instruction without any arguments. If the instruction sets the /ans
slot to True
, it then calls the second instruction without any arguments, and so on. while
calls both instructions in the current object so code in the instructions can modify the slots in the current object.
... /i 1 . ... while ( lt-eq i 10 ) ( print i . incr i . ) . 1 2 3 4 5 6 7 8 9 10 ans = False. ... print i . 10
The repeat
operation executes an instruction a fixed number of times in the current object. For example, the same loop could have been written as:
... /i 1 ... repeat 10 ( print i . incr i . ) . 1 2 3 4 5 6 7 8 9 10
The repeat
loop is faster than the while
loop when it is applicable because it does not need to check a condition.
Prototyping
SLOBIL is a prototyped-based object language. New objects can inherit from other objects using the new
operation. When changes are made to the parent object, it propogates to all the children. The new
operation also adds to (or replaces) the slots included from their parent object in an analogous way to the object
operation.
... /+rectangle+ [ object /area ( mul l w ) ] . ... /rect1 [ new +rectangle+ /l 5 /w 4 ] . ... print rect1:/area . ( mul l w ) ... set /d 2 +rectangle+ . ... set /area ( mul l w d ) +rectangle+ . ... print rect1:/area ( mul l w d )
The in
operation executes code inside a given object. It can be used to do what other languages call "method calls".
... in rect1 rect1:/area . ans = 40
Without using in
, calling rect1:/area
will not reference the values at the /l
and /w
slot in the object at /rect1
.
... rect1:/area . Error: Value at slot /l not found.
Calling instructions within an object is a common thing to do. So SLOBIL provides a convenience syntax. If the first element of a statement is an object, it calls the instruction at the slot provided in the first argument from within the object.
... rect1 /area . ans = 40
The link between objects created by new
is a link between the objects themselves, not the slots at which the objects are inserted. If the parent object is moved, deleted, or the slot is overwritten with a new object, it will not change the children because the object is simply removed from the slot it was located in originally, the object itself has not changed.
... /+rectangle+ [ object /area ( mul w l ) ] . ... /rect1 [ new +rectangle+ /l 5 /w 4 ] . ... rect1 /area . ans = 20 ... move /+rectangle+ /x . ... rect1 /area . ans = 20 ... /+rectangle+ [ object /area ( add l w ) ] . ... rect1 /area . ans = 20 ... delete /x . ... rect1 /area . ans = 20
Applying Instructions To Data In An Object
The do
operation applies an operation to each slot in a set of objects. For example:
... do add [ object /x 2 /y 3 /z 4 ] [ object /x 5 /y 2 ] . ans = an Object with: x of type Integer, value: 7 y of type Integer, value: 5
For each slot that has data in all the objects passed to do
as arguments, do
calls the operation in its first argument using the data from the given slot in each object as positional arguments, in the order the objects are given to do
.
do
takes operations as arguments, but programmers write instructions, not operations. Instructions can be converted to operations by equipping their code with a fixed set of positional arguments using the op
operation.
... do [ op ( div [ sub y x ] x ) /x /y ] [ object /x 2.0 /y 3.0 /z 4.0 ] [ object /x 5.0 /y 2.0 ] . ans = an Object with: x of type Real, value: 1.500000 y of type Real, value: -0.333333
The op
operation converts the instruction given into an operation by setting the slots in the temporary environment in which an instruction is called to the given positional arguments, e.g. in the above, the first argument will be inserted into the slot /x
and the second argument to the slot /y
.
Objects As Arrays
There is no array type in SLOBIL. Objects are used as array as well. Several operations make use of a slot-naming convention to treat objects like arrays. Slots that match the pattern /name1
, /name2
, …, /nameN
can be treated like an array by operations.
The next
operation sets the /ans
slot to the next slot given the current slot.
... next /c1 . ans = /c2
The previous
operation sets the /ans
slot to the previous slot given the current slot.
... previous /c10 . ans = /c9
The last
operation sets the /ans
slot to the last slot in an object that begins with the given prefix.
... /array [ list 1 2 3 ] . ... print array . an Object with: t1 of type Integer, value: 1 t2 of type Integer, value: 2 t3 of type Integer, value: 3 ... last array /t . ans = /t3
This example demonstrates the list
operation as well which creates a list-like object with data located at slots /t1
, /t2
, …, /tN
.
The collapse
operation can be used to summarize a list.
... /array [ list 3 -1 4 ] . ... collapse add array /t . ans = 6
collapse
applies an operation that takes two arguments to each element of the list consecutively. So, in the above example, first add 3 -1
is computed. Then, add ans 4
is computed, and so on.
collapse
and last
both operate on the /t
prefix by default so the last argument can be omitted if that is the prefix desired,
Exceptions and Error Handling
please
is the SLOBIL operation for handling errors. It takes two arguments. The first is an instruction to execute in the current object. The second is an instruction to execute if the first instruction fails.
Note that errors will still be printed to the console unless you run interpreter /print-errors False .
first.
Saving and Loading Objects
Objects can be saved with the save
operation. save
saves the current object to the given filepath.
... /test [ object /x 1 /y "hello" /z True ] . ... in test ( save "test.dslob" ) .
The build
operation saves objects in a standard location, ~/.dslobs
.
... build "test" test .
The above statement saves the object test
to ~/.dslobs/test.dslob
.
Similarly, the load
operation loads a given filepath into the current object, while the use
operation imports an object from the standard location for save files, ~/.dslobs
.
... load "test.dslob" . ... use "test" .
REPL Settings
The REPL itself has a few settings that can be configured using the interpreter
operation. The below example demonstrates how to change the various values from their defaults.
... interpreter /print-ans True . ... interpreter /auto-rehash False . ... interpreter /print-errors False .
The print-ans
setting indicates whether to print what the /ans
slot is after running each command. The default is not to do so.
The auto-rehash
setting indicates whether objects — which, internally, are hash tables — should be automatically rehashed as they grow in size or whether the rehash
operation should be used explicitly. The default is to rehash automatically.
The print-errors
setting indicates whether to print the trace of the error out when an error is encountered. The default is to do so.
Chaining operations
SLOBIL has an operation called infix
(alias: $
) that can be used to simulate infix notation when it is convenient. It expects arguments with the pattern DATA OPERATION DATA OPERATION DATA ...
. It runs the operations on the first two data objects and then uses the result stored in the /ans
slot for the first argument and takes the next data element as the second argument and applies the next operation to the two. It is primarily useful for arithmetic that is easier to read in this format.
... add 1 [ sub 3 2 ] . ans = 2 ... + 1 [ - 3 2 ] . ans = 2 ... infix 1 + 3 - 2 . ans = 2 ... $ 1 + 3 - 2 . ans = 2 ... add 1 [ mul 3 2 ] . ans = 7 ... + 1 [ * 3 2 ] . ans = 7 ... $ 1 + [ $ 3 * 2 ] . ans = 7 ... $ 1 + 3 * 2 . ans = 8
The last line illustrates a limitation of this convenience syntax. It has no notion of precedence. It simply changes the order in which the operations and data are interpreted.
Infix-notation is not very SLOBIL-like so it should be used sparingly, but it is occasionally useful for arithmetic expressions that otherwise might look overly complex.
Multi-threaded Programs
Tasks model processes. They enable multi-threaded programming.
The task
operation creates a task. The task
operation takes three arguments. The first gives the slot to assign the Task to. The second gives the body of the Task, an instruction that the Task will execute when it is run (Tasks do not immediately start running on creation). The third argument is the initial state of the task, a object defining the environment in which the task will run.
... task /my-task ( commands to run... ) [ object . ] .
The run-task
operation starts a task. It takes a single argument, the task to start running. Running a task means that the instruction that forms the body of the task is executed. Only one instance of the task can be running at a given time but the same task can be executed multiple times. The state of the task will not revert to its original state when it is run again.
The structure of a task is described in the below table:
Element | Description |
---|---|
Body | The instruction run in a separate thread from the main SLOBIL thread. |
State | A Object that contains the current state of the task. The object being modified by the Task's body. |
Queue | A Object that contains data queued to pass from the main thread to the Task's thread or vice-versa. |
A Task's state object is entirely separate from the current object. There is no way to directly access data in a task except in the body of the task. No data in the Task's queue can be accessed directly in any thread. Data can be sent between the main thread and the task thread using operations.
Tasks accept data when they take data from their queue and move it into their state object. They can do this with one of three operations: accept
, accept-or
, and select
.
The accept
operation takes a slot as an argument and waits for data to be available at that slot in the Task's queue. A Task can be provided as an optional second argument to accept
to look for the data in another task's queue. Once that data is available, it sets the /ans
slot in the Task's state object to the value of the data. accept
is blocking so no other code will be executed in the thread running the accept
operation until the data is available in the queue. SLOBIL does not allow running accept
in the main task because there is no way to exit the blocking. accept-or
can be used instead.
The queue
operation moves data from one object into the queue of another Task. Its arguments are a slot to set in the queue object, the data to send, and the task's queue to access.
... task /t1 ( print [ accept /hello ] . ) . ... run-task t1 . ... queue /hello "Hi!" t1 . Hi!
Because the accept
operation blocks the thread until the data is entered into the queue, it cannot be used when the data accept
is looking for is optional.
The accept-or
operation checks whether data exists at a certain slot in the queue, if it does, it sets the /ans
slot to the result. If not, it executes its second argument, an instruction, in its state object. This operation does not block. It checks once if the data is available and otherwise executes the instruction. For example, in a Task body, we might have:
accept-or /hello ( set /hello "Hello." ) .
The main use of accept-or
is to provide default values for missing data or to do something else if data is not available.
Alternatively, the select
operation executes one of several instructions if data comes in at any number of slots. select
takes paired slots and instructions as its arguments. It waits for data to appear in its queue at any of the slots listed. The first slot to receive data is "selected". The instruction associated with the slot is executed in the slot's state object after setting the /ans
slot to the value of the data located at the slot in the queue.
... task /t1 ( set /continue True . while ( answer continue ) ( select /add-one ( print [ add ans 1 ] ) /sub-one ( print [ sub ans 1 ] ) /continue ( set /continue ans ) . ) . ) . ... run-task t1 . ... queue /add-one 3 t1 . 4 ... queue /sub-one 3 t1 . 2
3. Example Programs
Accounting System
SLOBIL programs can use SLOBIL itself as their means of interacting with the user because the language is "command-like". This section describes a simple double-entry accounting program written in SLOBIL.
Interactive programs are usually created as objects so they can be easily loaded into SLOBIL with both state variables and instructions. So we start by defining the program object which will contain a list of accounts.
/accounting [ object /account-list [ object ] ] .
Next, we define a generic account object and add it to accounting
. I use +name+
as a naming convention for base objects that will be used as prototypes for subsequent objects.
/+account+ [ object /add-entry ( last entries /entry . next ans . set ans entry entries . incr balance entry:/amount . ) /summary ( print [ combine name ": " balance ] . ) /print-entry ( print [ combine entry:/desc ": " [ to-string entry:/amount ] ] . ) /print-entries ( do [ op print-entry /entry ] entries ... ) ] accounting .
To suppress printing out the /ans
slot, you can end a statement with three periods ...
.
Now, we need an instruction for creating a new account. We will have the user specify the account as a slot.
/new-account ( set name [ new +account+ /name [ to-string name ] /balance balance /entries [ object ] ] account-list . ) accounting .
This instruction might not seem to do much beyond what the new
operation does. One advantage of defining this instruction is that it will give an intelligible error if the user does not specify a name for the account and an opening balance. The new
operation doesn't expect any particular slots to be set, but the new-account
instruction does.
We next need an instruction to transfer money between two accounts. Let's define one.
/transfer ( /to-entry [ object /desc desc /amount amount ] . /from-entry [ object /desc desc /amount [ mul -1 amount ] ] . if [ exist from-account account-list ] { account-list:from-account /add-entry /entry from-entry . } { if [ exist to-account account-list ] { account-list:to-account /add-entry /entry to-entry . } . } . ) accounting .
Lastly, let's add some instructions to summarize our accounts and print out entries in a given account.
/summary ( do [ op ( account /summary ) /account ] account-list ... ) accounting . /entries ( account-list:account /print-entries . ) accounting .
Let's see the program in action.
... accounting /new-account /name /Checking /balance 1000.50 . ... accounting /new-account /name /Savings /balance 20000.00 . ... accounting /transfer /desc "Saving Money" /amount 100 /to-account /Savings /from-account /Checking . ... accounting /summary . Savings: 20100.000000 Checking: 900.500000 ... accounting /entries /account /Checking . Saving Money: -100
The user shouldn't have to type accounting
every time. Temporarily, in the session, we can import
it, but to package it for users, it is better to use the build
operation to save the object to disk.
... build "accounting" accounting .
Now, whenever we start a SLOBIL session, we can simply type:
... use "accounting" . ... new-account /name /Checking /balance 1000.50 . ... summary .
A Time-Keeping Program
I used this program to keep track of billable hours for about three years. It is certainly the most used SLOBIL program as of June 2023.
' Initial state /current-thing "Nothing" . /last-clock -1 . /record [ object ] . /running False . /current-file Nothing . ' Start a new record of work or load an old one /init [ op ( up set /current-file file . please ( load file record ) ( up set /record [ object ] . up set /thing-list [ object ] record . print "Starting new file." . ) . ) /file ] . ' Save record of work in SLOBIL format /save-hours ( in record ( save current-file ) . ) . ' Set current thing /thing [ op ( if [ not running ] { if [ exist /name ] { up set /current-thing name . add-thing-to-list name . } { up set /current-thing [ choose-thing ] . } . up set /last-clock -1 . } { print "Currently running, stop first." . } . ) /name ] . ' Start clock for current thing /start ( if [ not running ] { up set /last-clock [ clock ] . up set /running True . print [ combine "Starting on " current-thing " ..." ] . } { print "Currently running, stop first." . } . ) . ' Stop clock for current thing with a description of task. /stop ( if running { last record /r . next ans . set ans [ object /thing current-thing /start-time last-clock /stop-time [ clock ] /description desc ] record . up set /running False . save-hours . } { print "Not currently running." . } . ) . ' A map to day of week /day-of-week-map [ list "Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday" ] . ' Convert to hours /make-hours-day-time [ op ( do [ op ( div t 1000.0 60 60 ) /t ] structure . ) /structure ] . ' Return a structure describing a single day of activity /report [ op ( /final [ object ] . /clock1 [ make-clock [ object /day day /month month /year year /hour 0 /minute 0 /seconds 0 /milliseconds 0 ] ] . /clock2 [ make-clock [ object /day day /month month /year year /hour 23 /minute 59 /seconds 59 /milliseconds 999 ] ] . /i /r1 . while ( exist i record ) ( if [ and [ gt-eq record:i:/start-time clock1 ] [ lt-eq record:i:/start-time clock2 ] ] { /thing [ to-slot record:i:/thing ] . /elapsed [ sub record:i:/stop-time record:i:/start-time ] . if [ exist thing final ] { set thing [ add final:thing elapsed ] final . } { set thing elapsed final . } . } . /i [ next i ] . ) . make-hours-day-time final . ) /year /month /day ] . ' Add thing to thing list /add-thing-to-list [ op ( if [ not [ exist /thing-list record ] ] { set /thing-list [ object ] record . } . last record:/thing-list /c . next ans . set ans thing record:/thing-list . ) /thing ] . ' Select thing from menu /choose-thing ( /i /c1 . while ( exist i record:/thing-list ) ( print [ combine [ to-string [ to-number i ] ] ": " record:/thing-list:i ] . /i [ next i ] . ) . print "Choose Thing: " False . input /num . /loc [ to-slot [ combine "c" num ] ] . answer record:/thing-list:loc . ) . ' Print status /status ( print [ combine "Current thing: " current-thing ] . if running { print "Running a task." . } { print "Not running." . } . ) . ' Update thing in list /rename-thing ( /i /c1 . /cont True . while ( and [ exist i record:/thing-list ] cont ) ( if [ string-eq record:/thing-list:i thing ] { set i new-thing record:/thing-list . /cont False . } . /i [ next i ] . ) . ' Update thing in records /i /r1 . while ( exist i record ) ( if [ string-eq record:i:/thing thing ] { /thing new-thing record:i . } . /i [ next i ] . ) . ) . ' Rewind X minutes from start /start-back [ op ( if [ not running ] { print "Currently not running." . } { up set /last-clock [ sub last-clock [ mul x 60 1000 ] ] . } . ) /x ] . ' Rewind X minutes from last entry /rewind-last [ op ( if running { print "Currently running." . } { /end [ last record /r ] . /stop-time [ sub record:end:/stop-time [ mul x 60 1000 ] ] record:end . } . ) /x ] .
4. Example Instructions
Quicksort
set /swap ( set /tmp input:i . set i input:j input . set j tmp input . ) . set /partition ( set /pivot input:end . set /i start . set /j start . set /cont True . while ( answer cont ) ( if [ compare input:j pivot ] { swap /input input /i i /j j . set /i [ next i ] . } . if [ slot-eq j end ] { set /cont False . } . set /j [ next j ] . ) . swap /input input /i i /j end . answer i . ) . set /-quicksort ( if [ not [ slot-eq start end ] ] { partition /input input /start start /end end /compare compare . move /ans /p . if [ not [ slot-eq p start ] ] { -quicksort /input input /start start /end [ previous p . ] . } . if [ not [ slot-eq p end ] ] { -quicksort /input input /start [ next p . ] /end end . } . } . ) . set /quicksort ( if [ not [ exist /end ] ] { set /end [ last input slice . ] . } . if [ not [ exist /start ] ] { set /start [ next slice . ] . } . if [ not [ exist start input ] ] { error "Starting slot not found in input." . } . if [ not [ exist end input ] ] { error "Ending slot not found in input." . } . -quicksort /input input /start start /end end /compare compare . ) .
Example of use:
... set /x [ list 3 2 4 1 ] . ... quicksort /input x /slice /t /compare lt . ... print x . ans = a object with: t1 of type Integer, value: 1 t2 of type Integer, value: 2 t3 of type Integer, value: 3 t4 of type Integer, value: 4 ... quicksort /input x /slice /t /compare gt . ... print x . a object with: t1 of type Integer, value: 4 t2 of type Integer, value: 3 t3 of type Integer, value: 2 t4 of type Integer, value: 1
A Line-Oriented Text Editor
This example is a simple interactive program where the user enters commands to edit a buffer.
' Line-oriented text editor /slobed [ object . ] . in slobed ( /buffer [ list . ] . /cur-line /t1 . /n ( up set /cur-line [ next cur-line . ] . ) . /p ( up set /cur-line [ previous cur-line . ] . ) . /i [ op ( /arbed-str s buffer . in buffer ( set cur-line arbed-str . ) . ) /s ] . /o ( print [ get cur-line buffer . ] . ) . ) .
Example of use:
... import slobed . ... i "Hello" . ... o . Hello ... n . ... i "Goodbye" . ... o . Goodbye ... p . ... o . Hello
Numerical Differentiation
' Compute the numerical derivative of an operation that takes an arg set /deriv ( if [ not [ exist /h ] ] { set /h 0.001 . } . div [ sub [ f [ add x h ] ] [ f [ sub x h ] ] ] [ mul 2 h ] . ) .
Example of use:
... set /f [ op ( power t 2 ) /t ] . ... deriv /f f /x 1.0 ans = 2.0
Compute Factorial
' compute a factorial using while set /factorial [ op ( /i 2 . /prod 1 . while ( lt-eq i t ) ( /prod [ mul i prod ] . incr i . ) . answer prod . ) /t ] . ' compute factorial using recursion set /-factorial-recurse ( if [ eq t 1 ] 1 { mul t [ -factorial-recurse /t [ sub t 1 ] ] . } . ) . set /factorial-recurse [ op -factorial-recurse /t ] .
Example of use:
... factorial 5 . ans = 120 ... factorial-recurse /t 5 . ans = 120
Note on Recursion
Operations cannot be directly defined recursively, but Instructions can be. That is why the code above first defines -factorial-recurse
as an instruction and then creates the operation.
Numerical Integration
' takes an operation as an argument and return left hand ' riemmann sum over range set /integrate ( /delta [ div [ sub ub lb ] n ] . /x lb . /int 0.0 . repeat n ( incr int [ f x ] . incr x delta . ) . mul int delta . ) .
Example of use:
... integrate /f [ op ( power t 2 ) /t ] /lb 0.0 /ub 1.0 /n 1000 . ans = 0.332834
Basic Linear Algebra
This example demonstrates both application methods like do
and collapse
as well as when to use operations for programmer code. Operations make more sense when the arguments are all the same kind of thing, the order is the only thing that might matter, like in this example where we are multiplying two matrices. Instructions are preferred when the argument's position is uninformative about the purpose of the argument, as in the previous example with numerical integration.
' Computes v^T w for a vector set /dotproduct [ op ( do mul v w . collapse add ans . ) /v /w ] . ' matrix-multiply A B computes A^T B set /matrix-multiply [ op ( set /i /t1 . set /out [ list ] . while ( exist i B ) ( do [ op ( dotproduct t1 B:i ) /t1 ] A . in out ( set i ans ) . next i . move /ans /i . ) . answer out . ) /A /B ] . ' Computes A^T set /transpose [ op ( set /i /t1 . set /out [ list ] . while ( exist i A:/t1 ) ( do [ op ( answer t1:i ) /t1 ] A . move /ans /res . in out ( set i res ) . next i . move /ans /i . ) . answer out . ) /A ] .
Example of use:
... set /A [ list [ list 1 2 ] [ list 3 4 ] ] . ... set /B [ list [ list 6 2 ] [ list 3 4 ] ] . ... transpose A . ans = a object with: t1 of type Object, value: a object with: t1 of type Integer, value: 1 t2 of type Integer, value: 3 t2 of type Object, value: a object with: t1 of type Integer, value: 2 t2 of type Integer, value: 4 ... matrix-multiply A B . ans = a object with: t1 of type Object, value: a object with: t1 of type Integer, value: 10 t2 of type Integer, value: 26 t2 of type Object, value: a object with: t1 of type Integer, value: 11 t2 of type Integer, value: 25
Split a String
' Tokenize a string /line by splitting on string /sep set /tokenize ( set /i 1 . set /len [ length line . ] . set /cur-reg /t1 . set /tokened [ list . ] . set /init True . while ( lt-eq i len . ) ( set /d [ substring line i i . ] . if [ string-eq d sep . ] { in tokened ( set cur-reg buffer . ) . set /cur-reg [ next cur-reg . ] . set /init True . } { if init { set /buffer d . set /init False . } { set /buffer [ combine buffer d . ] . } . } . set /i [ add i 1 . ] . ) . in tokened ( set cur-reg buffer . ) . answer tokened . ) .
Examples of use:
... tokenize /line "1,2,3" /sep "," . ans = a object with: t1 of type String, value: 1 t2 of type String, value: 2 t3 of type String, value: 3
Concurrent Programming
SLOBIL uses Tasks to run Instructions in separate processes. The following example demonstrates how queue
, accept
, and select
work to move data between the main Task and other Tasks.
' Demonstrate tasks task /t1 ( while ( answer True . ) ( queue /current [ accept /hello . ] . ) . ) [ object . ] . run-task t1 . queue /hello "Hi!" t1 . accept-or /current ( print "Result not ready yet." . ) t1 . task /t2 ( set /continue True . while ( answer continue . ) ( select /double ( print [ mul ans 2 . ] . ) /triple ( print [ mul ans 3 . ] . ) /end ( set /continue False . ) . ) . ) [ object . ] . run-task t2 . queue /double 3 t2 . queue /triple 10 t2 . queue /double 7 t2 . queue /end Nothing t2 .
The following task that can be used to run any code in the background.
task /t1 ( /instr [ accept /instruction ] . print "HELLO" . accept /context . import ans . instr . queue /result ans . ) [ object ] . run-task t1 . queue /instruction ( add x y ) t1 . queue /context [ object /x 2 /y 4 ] t1 . accept-or /result ( print "Result not ready yet." ) t1 . accept-or /result ( print "Result not ready yet." ) t1 .
Linking C code
One of the big outstanding tasks with SLOBIL is to write a more user-friendly (and documented) libslobil for adding extensions, but it is currently possible to write SLOBIL operations in C, using SLOBIL's link
operation to import the code.
The following is an example of C code.
/* A C Function to compute log(x+1) */ /* Change to wherever slobil.h is located or just "slobil.h" if it is on the path */ #include "../src/slobil.h" #include <math.h> void log_plus_one (arg a, object* reg) { check_length(&a, 2, "log1", reg->task->task); if (is_error(-1, reg->task->task)) return; data* arg1 = resolve(a.arg_array[1], reg); if (arg1 == NULL) { do_error("<log1> requires an argument.", reg->task->task); return; } if (arg1->type != Integer && arg1->type != Real) { do_error("Argument to <log1> must be numeric.", reg->task->task); return; } double ans; if (arg1->type == Integer) { double z = mpz_get_d(*((mpz_t*) arg1->data)); ans = log(z + 1.0); } else { ans = log(*((double*) arg1->data) + 1.0); } data* d; assign_real(&d, ans); ret_ans(reg,d); }
We then compile it into a shared object file, linking against libslobil
.
#!/bin/sh ## Assumes you have already make installed briple so that libbriple is on your path and that the ## library is installed in /usr/local/lib. Modify path if installed elsewhere. gcc -c -fPIC -o link.o link.c gcc -shared -Wl,-rpath -Wl,/usr/local/lib -o link.so link.o -lslobil
The code then can be loaded into SLOBIL with the link
operation like so:
' Demonstrates linking C code in link "./link.so" "log_plus_one" "log1" .
The first argument of link
gives the shared-object file name. The second argument gives the name of the function. The third argument gives what the name of the SLOBIL operation will be.
Now, the operation can be called in the following way:
... log1 1 . ans = 0.693147
5. Reference
This section gives the full vocabulary of the language. The grammar of the language is described in the previous sections. I describe each type of data, and the operations associated with it.
Throughout, I enclose an operation's optional arguments between bars (||).
Objects
Short description
A object contains data located at slots. Data can be retrieved from and inserted into objects. Objects also defined the scope of SLOBIL code because code is executed inside objects. So the values of variables depend on which object you are executing the code. This allows the user to flexibly manipulate scope.
Implementation details
Objects are hash tables. The keys to the table are called slots. The number of bins in the hash table grows as more elements are added. This keeps lookup times low, but the object will rehash as it grows. Rehashing can be turned off by using the auto-rehash
operation.
Object operations
- Creation
object
Usage:
object SLOT1 VALUE1 SLOT2 VALUE2 ...
Sets the
/ans
slot to a object withVALUE1
located atSLOT1
and so on.list
Usage:
list VALUE1 VALUE2 ...
Set the
/ans
slot to a Object withVALUE1
at Slot/t1
,VALUE2
at Slot/t2
, and so on.range
Usage:
range INTEGER1 INTEGER2 |INTEGER3|
Set the
/ans
slot to a Object withINTEGER1
at/t1
andINTEGER1 + INTEGER3
at/t2
and so on so long as the value is less than or equal toINTEGER2
.
- Insert, move, and remove data to and from slots
set
Usage:
set SLOT VALUE |OBJECT|
Sets the value at
SLOT
toVALUE
in the objectOBJECT
. If theOBJECT
argument is omitted, then it will set the slot in the current object.move
Usage:
move SLOT1 SLOT2
Move the value located at
SLOT1
toSLOT2
. Does not copy the data so it is an efficient way to move data.delete
Usage:
delete SLOT
Delete the value at
SLOT
in the current object.
- Access data in Object
get
Usage:
get SLOT |OBJECT|
Sets the
/ans
slot to the value located atSLOT
inOBJECT
. If theOBJECT
argument is not specified, get from the current object.exist
Usage:
exist SLOT |OBJECT|
Set the
/ans
slot toTrue
if a value exists at theSLOT
inOBJECT
. If theOBJECT
argument is omitted, check in the current object.import
Usage:
import OBJECT
Set the Slots in the current Object to hold the same values that they hold in
OBJECT
.filter
Usage:
filter OBJECT OPERATION
Set the
/ans
slot to a Object containing all the elements inOBJECT
such that theOPERATION
applied to the element of the Object sets the/ans
slot toTrue
.
- Apply Operations to elements of a Object
do
Usage:
do OPERATION OBJECT1 OBJECT2 ... OBJECTN
Execute
OPERATION
which takes its arguments (1…N) from each element inOBJECT1
, …,OBJECTN
. Sets the/ans
slot to a Object which contains whatever theOPERATION
evaluates to at the corresponding Slots. For example,do add [ list 1 2 3 . ] [ list 4 5 6 . ]
sets the/ans
slot to a Object with elements (5,7,9) at slots(/t1,/t2,/t3)
.do
will only return results at Slots that exist in all Objects. So, for example,do add [ list 1 2 3 . ] [ list 4 5 . ]
sets the/ans
Slot tolist 5 7
.collapse
Usage:
collapse OPERATION OBJECT |SLOT|
Sets the
/ans
slot to the value of applyingOPERATION
to the value in theOBJECT
at Slot/SLOT1
and/SLOT2
and then again to the result of that and to the value of/SLOT3
and so on. For example,collapse add [ list 1 2 3 . ] /t
sets the/ans
slot to6
. If theSLOT
argument is omitted, it is assumed to be/t
.
- Execute code in a object
in
Usage:
in OBJECT INSTRUCTION
Call
INSTRUCTION
inOBJECT
.up
Usage:
up STATEMENT
Execute STATEMENT in the object above the current one after resolving the values of the arguments of the statement in the current object. For example, we can create an Instruction that can be used to increment a variable in place, like so:
... set /inc ( up set x [ add 1 [ up get x . ] . ] . ) . ... set /x 1 . ... inc /x /x . ... print x . 2
- Test if a object
- Technical
Reals and Integers
Short description
Real and Integer are distinct types in SLOBIL. A Real is a number with a decimal point. An Integer is a number without a decimal point. Sometimes either type can be used for an operation. In this case, I refer to the argument type as a Number in describing the operation for brevity. Number is not a SLOBIL type.
Implementation details
A Real is a double-precision floating point value (equivalent to C's double).
An Integer is an arbitrarily long integer (a "bignum"). Integers are implemented using GnuMP.
Real and Integer operations
- Arithmetic operations
add
Usage:
add NUMBER1 NUMBER2 ...
Adds all the numbers together and sets the
/ans
slot to the result.mul
Usage:
mul NUMBER1 NUMBER2 ...
Multiplies all the numbers together and sets the
/ans
slot to the result.sub
Usage:
sub NUMBER1 NUMBER2 ...
Subtracts the second number from the first and the third number from that and so on and sets the
/ans
slot to the result.div
Usage:
div NUMBER1 NUMBER2 ...
Divides the first number by the second, the result by the third number, and so on and sets the
/ans
slot to the result.mod
Usage:
mod NUMBER1 NUMBER2
Return the remainder of dividing the first number by the second.
incr
Usage:
incr INTEGER1 |INTEGER2|
Increment
INTEGER1
byINTEGER2
or by 1 if INTEGER2 is omitted. The difference between this operation andadd
is that it modifiesINTEGER1
in place.INTEGER2
can be a negative number.
- Comparison operations
gt
Usage:
gt NUMBER1 NUMBER2
Set the
/ans
slot toTrue
ifNUMBER1
is greater thanNUMBER2
and toFalse
otherwise.lt
Usage:
lt NUMBER1 NUMBER2
Set the
/ans
slot toTrue
ifNUMBER1
is less thanNUMBER2
and toFalse
otherwise.eq
Usage:
eq NUMBER1 NUMBER2
Set the
/ans
slot toTrue
ifNUMBER1
is equal toNUMBER2
and toFalse
otherwise.lt-eq
Usage:
lt-eq NUMBER1 NUMBER2
— set the/ans
slot toTrue
ifNUMBER1
is less than or equal toNUMBER2
and toFalse
otherwise.gt-eq
Usage:
gt-eq NUMBER1 NUMBER2
Set the
/ans
slot toTrue
ifNUMBER1
is greater than or equal toNUMBER2
and toFalse
otherwise.
- Conversion operations
- Test if type operations
- Common mathematical operations
log
Usage:
log NUMBER
Set the
/ans
Slot to the natural logarithm ofNUMBER
.exp
Usage:
exp NUMBER
Set the
/ans
Slot to theNUMBER
power of the natural base.power
Usage:
power NUMBER1 NUMBER2
Set the
/ans
Slot toNUMBER1
raised to theNUMBER2
power.floor
Usage:
floor REAL1
Set the
/ans
Slot to the greatest Integer less thanREAL1
.ceiling
Usage:
ceiling REAL1
Set the
/ans
Slot to smallest Integer greater thanREAL1
.
Strings
Short description
Strings in SLOBIL are text. A string literal is enclosed in quotation marks. SLOBIL supports UTF-8 encodings. Non-ASCII characters are handled correctly: a single character is a single character no matter how many bytes it is. It also implements a collection of escape sequences for whitespace and quotation marks that should be included in the string itself.
The escape sequences are:
Escape Sequence | Meaning |
---|---|
\\ | \ |
\' | ' (single-quote) |
\t | tab |
\n | newline |
\r | carriage return |
' (single-quote) | " (double quote) |
Implementation details
Strings are internally UTF-32 encoded to enable faster access by index number and because it makes creating substrings faster. SLOBIL accepts UTF-8 as input because that is more common in terminals and in file-encodings.
String operations
- Access, search, and modify string elements
substring
Usage:
substring STRING INTEGER1 INTEGER2
Sets the
/ans
Slot to the subset ofSTRING
where the characters included are determined byINTEGER1
andINTEGER2
. Strings are 1-indexed in SLOBIL so the first character is at location 1. If theINTEGER
is less than or equal to 0, determine the location from the end of the String. So ifINTEGER1
andINTEGER2
are0
, then it will set the/ans
slot to the last character inSTRING
.match
Usage:
match STRING1 STRING2 |INTEGER|
Find occurences of regular expression
STRING1
in StringSTRING2
. Sets the/ans
Slot to a Object with all submatches located at the/tN
objects. The optional third argument gives the number of matches to return. If0
is provided, return all matches (default).replace
Usage:
replace STRING1 STRING2 STRING3 |INTEGER|
Set the
/ans
Slot to a String formed by replacing all occurences of regular expressionSTRING1
withSTRING2
inSTRING3
. The optional fourth argument gives the number of matches to replace. If0
, replace all (default).
- String properties
- String comparison
string-eq
Usage:
string-eq STRING1 STRING2
Sets the
/ans
Slot toTrue
if the two strings are equal and toFalse
otherwise.string-lt
Usage:
string-lt STRING1 STRING2
Sets the
/ans
Slot toTrue
ifSTRING1
is less thanSTRING2
in the sense that the ASCII characters have lower values.string-gt
Usage:
string-gt STRING1 STRING2
Sets the
/ans
Slot toTrue
ifSTRING1
is greater thanSTRING2
in the sense that the ASCII characters have lower values.char-eq
Usage:
char-eq STRING1 INTEGER STRING2
Test if character
INTEGER
of string (using same index numbers as forsubstring
) is equal toSTRING2
. This is a slight performance improvement over testing whether thesubstring
with only one character is equal to the string.
- Combine strings
- Convert to string
to-string
Usage:
to-string INTEGER|REAL|SLOT |INTEGER2|
Sets the
/ans
Slot to a String representing theINTEGER
orREAL
orSLOT
provided as the first argument. The second argument is used if aREAL
argument is provided.INTEGER2
gives the number of elements after the decimal point to include. If not provided, 6 decimal places are included.
- Test if string
- Strings from User Input
Slots
Short description
Slots are a type of data in SLOBIL usually used to refer to locations in slots. More generally, they are symbols. They can be compared with one another and modified. Slots are not "associated" with objects. They are data without any associated context.
Implementation details
Slots are a structure containing the name of the object and the hashed value of the name so they can be easily inserted into the object hash table.
Slot operations
- Operations on "list" slots
next
Usage:
next SLOT
If the
SLOT
ends in a number, return the Slot with that number incremented by 1.previous
Usage:
previous SLOT
If the
SLOT
ends in a number, return the Slot with that number minus 1. If that would cause the number to be less than 1 set the/ans
Slot to the "first" slot with that prefix so thatprevious /t1 .
sets/ans
to/t1
.last
Usage:
last OBJECT SLOT
Return the last Slot in the
OBJECT
that starts withSLOT
. So that if you had slots/x0
,/x1
, and/x2
in the Object,last OBJECT /x
would return/x2
.
- Convert to slot
- Slot comparison
- Test if a slot
Booleans
Short description
Booleans can take on two values: True
or False
. Comparison and testing operations usually set the /ans
slot to a Boolean value.
Implementation details
Booleans are simply a binary value. They use the C99 bool type internally.
Boolean operations
is-boolean
Usage:
is-boolean VALUE
Sets the
/ans
Slot toTrue
if theVALUE
is a Boolean and toFalse
otherwise.and
Usage:
and BOOLEAN1 BOOLEAN2
Sets the
/ans
Slot toTrue
if BOOLEAN1 and BOOLEAN2 areTrue
and toFalse
otherwise.or
Usage:
or BOOLEAN1 BOOLEAN2
Sets the
/ans
Slot toTrue
if either BOOLEAN1 or BOOLEAN2 areTrue
and toFalse
otherwise.not
Usage:
not BOOLEAN
Sets the
/ans
Slot toTrue
if BOOLEAN isFalse
and toFalse
otherwise.
Instructions
Short description
Instructions are code objects which have not yet been executed. They can be called or executed in different objects.
Implementation details
Instructions are internally "compiled" code. They are then supplied with an environment (a object) before they are executed. The compiled code object is a linked list of statements each of which is a linked list of elements. Elements are either literal data values or the names of slots to lookup in the environment.
Instruction operations
call
Usage:
call INSTRUCTION SLOT1 VALUE1 SLOT2 VALUE2 ...
Executes
INSTRUCTION
in a Object withSLOT1
assigned toVALUE1
and so on.is-instruction
Usage:
is-instruction VALUE
Sets the
/ans
Slot toTrue
if theVALUE
is an Instruction and toFalse
otherwise.op
Usage:
op INSTRUCTION SLOT1 ... SLOTN
Set the
/ans
slot to an operation that callsINSTRUCTION
after bindingSLOT1
to the first argument, and so on.code
Usage:
code INSTRUCTION
Sets the
/ans
slot to a String containing the code for theINSTRUCTION
.is-operation
Usage:
is-operation VALUE
Sets the
/ans
Slot toTrue
if theVALUE
is an Operation and toFalse
otherwise.set-help
Usage:
set-help INSTRUCTION STRING
Sets the help for
INSTRUCTION
toSTRING
.help
Usage:
help INSTRUCTION
Print the help string.
bind
Usage:
bind INSTRUCTION SLOT VALUE SLOT VALUE ...
Modifies the environment the instruction executes in so that INSTRUCTION now has slot values fixed to the SLOT VALUE pairs. For example, suppose we have an instruction
sum
defined as( add t s )
. Then, afterbind sum /t 2
,/sum /s 3
will set the/ans
slot5
.
Files
Short description
Files are stream objects that you can write to and read from.
Implementation details
Files are like the C FILE type.
File operations
is-file
Usage:
is-file VALUE
Sets the
/ans
Slot toTrue
if theVALUE
is a File and toFalse
otherwise.open-file
Usage:
open-file STRING1 |STRING2|
Sets the
/ans
Slot to a File object corresponding to the file namedSTRING1
with modeSTRING2
(default: "r+").read
Usage:
read FILE
Read a byte from a file and set the
/ans
slot to the result (an Integer).read-char
Usage:
read-char FILE
Read a single character from a File object and set the
/ans
Slot to that character (a String).read-line
Usage:
read-line FILE
Sets the
/ans
Slot to the next line from FILE.write
Usage:
write INTEGER FILE
Integer
must be in [0,255]. Directly writes a byte toFILE
.write-string
Usage:
write-string STRING FILE
Write a
STRING
toFILE
using UTF-8 encoding.close
Usage:
close SLOT
If a File object is located at
SLOT
, close the File and remove its reference from theSLOT
.
Nothing
Short description
Nothing
is a value that is not any other value and is a different type than any other value.
Task Operations
Short description
Tasks are objects that allow you to execute code in another thread and share data between threads.
Implementation details
Tasks contain three elements:
- A Body, an instruction that is executed when the Task is run.
- A State, a object that contains the state of the Task. The Body is run inside the State object. It is persistent across Task runs.
- A Queue, a object that cannot be reached directly but which facilitates data sharing across tasks without worrying about race conditions or other details.
Tasks use mutexes to ensure data is transferred correctly between threads.
Task operations
task
Usage:
task SLOT INSTRUCTION |OBJECT|
Sets
SLOT
to contain a new Task with its Body given by theINSTRUCTION
and the initial state being set toOBJECT
. If noOBJECT
argument is given, the initial state is a object containing only the basic operations (the object always contains the basic operations even if they are not in the initial object passed toTask
).run-task
Usage:
run-task TASK
Executes the Task's Body in a separate thread. The same Task object cannot be running in two threads. The operation will give an Error if it is already running.
queue
Usage:
queue SLOT DATA |TASK|
Sets the
SLOT
in the Queue ofTASK
to valueDATA
. If theTASK
argument is omitted, it is assumed to be set in the queue of the current task (for sharing Task data with another thread).queue
cannot be used for the main SLOBIL Task.accept
Usage:
accept SLOT |TASK|
Waits until
SLOT
has a value inTASK
's Queue. Once it finds a value, set the/ans
slot to that value. IfTASK
is omitted, wait for the current Task's Queue to be set.select
Usage:
select SLOT1 INSTRUCTION1 SLOT2 INSTRUCTION2 ... SLOTN INSTRUCTIONN
Waits for the Queue in the current task to contain data at any of the Slots (it will prefer earlier Slots if data is available at both). Once it receives data, it sets the
/ans
slot to the data and executes the Instruction in the current object.accept-or
Usage:
accept-or SLOT INSTRUCTION |TASK|
Checks if
SLOT
is set inTASK
's Queue. If it is, the/ans
slot is set to the result. If it is not, it executesINSTRUCTION
. IfTASK
is omitted, search in the currentTASK
's Queue.
Control flow operations
if
Usage: if BOOLEAN VALUE1 |VALUE2|
If the first argument is True
, sets the /ans
slot to VALUE1
, if it is False
, sets the /ans
slot to VALUE2
. If VALUE2
is omitted, do nothing if the first argument is False
.
while
Usage: while INSTRUCTION1 INSTRUCTION2
Call INSTRUCTION1
in the current Object. If it sets its /ans
slot to True
, call INSTRUCTION2
. Repeat.
repeat
Usage: repeat INTEGER INSTRUCTION
Call INSTRUCTION
INTEGER
times.
please
Usage: please INSTRUCTION1 INSTRUCTION2
Call INSTRUCTION1
. If there is an error, then call INSTRUCTION2
.
Environment operations
Modify the /ans slot
Print output
Input and output files and state
source
Usage:
source STRING
Executes SLOBIL code in the file named by STRING.
save
Usage:
save FILENAME
Save the contents of the current object to file.
load
Usage:
load FILENAME
Load the contents of a saved file into the environment.
output-code
Usage:
output-code STRING
Outputs the code that has been entered at the prompt so far to the file named STRING.
clear-code
Usage:
clear-code
Clear code that has been entered so far so that the code that
output-code
outputs will be empty.
Error handling
error
Usage:
error STRING |INTEGER|
Outputs error message String and sets an error code INTEGER (if specified, otherwise, the error number is
1
).is-error
Usage:
is-error INSTRUCTION
Sets the
/ans
Slot toTrue
if there was an error executing theINSTRUCTION
and toFalse
otherwise.ignore-errors
Usage:
ignore-errors INSTRUCTION
Ignore any errors while executing the instruction, i.e. go to the next statement if one statement errors.
System operations
Interacting with the shell
Time
clock
Usage:
clock
Set the
/ans
slot to the current time in milliseconds from January 1, 1970.make-time
Usage:
make-time INTEGER
Given a clock value in milliseconds (as returned by
clock
) return a object with elementssecond
(a number of seconds between 0-59),minute
(giving a number between 0-59),hour
(a number between 0-23),day
(giving a number between 1-31),make-clock
Usage:
make-clock OBJECT
Make a clock value in milliseconds from a object describing the time (like the object returned by
make-time
)
Package operations
use
Usage: use STRING
Load a saved object in ~/.dslobs/
with the extension .dslob
into the current object. The file being loaded is: ~/.dslobs/STRING.dslob
.
build
Usage: build STRING OBJECT
Saves contents of object to ~/.dslobs/STRING.dslob
so that it can be loaded as a package.
link
Usage: link STRING1 STRING2 STRING3
Loads shared object file STRING1
and its function named STRING2
into SLOBIL operation named STRING3
.
Interpreter options
List of options
print-ans
Usage:
/print-ans
If
True
, then print the value of the/ans
Slot at the end of commands, ifFalse
(default) or any other non-Boolean value, suppress printing.auto-rehash
Usage:
/auto-rehash
If
True
(default) or any non-Boolean value, then rehash objects automatically as they grow in size. IfFalse
do not rehash automatically.print-errors
Usage:
/print-errors
If
True
(default) or any non-Boolean value, then print error descriptions. ifFalse
, do not print error descriptions.
6. Saved Objects
Note: When saving, only "permanent" data is written to disk. For example, operations written in C will not be written to disk because these are linked in at run time. File values will also not be written to disk because the file stream would need to be re-opened after restarting SLOBIL in any case; it has no permanent information. All other data types are written to disk.
This section will describe the storage format for SLOBIL saved files. A user or programmer will never need to know the internal structure of the save file, but it will be provided here to have complete documentation and to perhaps be useful to people writing a C extension to SLOBIL.
7. GNU Free Documentation License
GNU Free Documentation License
Version 1.3, 3 November 2008
Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
0. PREAMBLE
The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
1. APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
The "publisher" means any person or entity that distributes copies of the Document to the public.
A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
2. VERBATIM COPYING
You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may publicly display copies.
3. COPYING IN QUANTITY
If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
4. MODIFICATIONS
You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
- A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
- B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
- C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
- D. Preserve all the copyright notices of the Document.
- E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
- F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
- G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
- H. Include an unaltered copy of this License.
- I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
- J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
- K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
- L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
- M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
- N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
- O. Preserve any Warranty Disclaimers.
If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties—for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
5. COMBINING DOCUMENTS
You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
8. TRANSLATION
Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
9. TERMINATION
You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.
10. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See https://www.gnu.org/licenses/.
Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.
11. RELICENSING
"Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.
"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.
"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.
An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.
The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.
ADDENDUM: How to use this License for your documents
To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
Copyright (C) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with … Texts." line with this:
with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.