Lace : The Aglet Scripting Language - hetland.org

mewstennisΛογισμικό & κατασκευή λογ/κού

4 Νοε 2013 (πριν από 3 χρόνια και 9 μήνες)

109 εμφανίσεις

Lace —the aglet scripting language
Magnus Lie Hetland
August 12,1998
1 Introduction
The language Lace is meant to ease the task of experimenting with mobile agents,
using IBM’s Aglet Software Development Kit,which is written in Java.If one knows
Java,it is a relatively simple matter to subclass the Aglet class,but if you don’t,
there isn’t really much you can do,except try out the examples that are included
in the package.Lace is conceptually simple and hopefully easy to learn
1
.Even
with modest programming skills it shouldbe possible to quickly master the basics
and to start experimenting with the aglet toolkit.
2 Basic language concepts
Lace is a postfix language,that is,its operators are written inReverse Polish Notation
(RPN).So the mathematical expression (1 * (2 + 3)) would be written
1 2 3 add mul.
This code is evaluated fromthe left —1 evaluates to 1,2 and 3 evaluate to 2 and
3;add uses 2 and 3 and evaluates to 5;mul uses 1 and 5 and evaluates to 5.
Lace is inspired by the page description language PostScript,and the two lan-
guages have very similar execution models.The virtual machine that interprets
the scripts is based around three stacks:The execution stack,the dictionary stack,
and the operand stack.
The execution stack keeps all the execution frames (scripts,procedures).When
a script is started,it is pushed onto the execution stack.When a procedure is
called,or a code block is otherwise executed (se below) it is also pushed onto
this stack.The execution always uses the frame that is topmost on the stack,so a
procedure would be finished and popped off before returning to the main script
again.
The dictionary stack keeps only dictionary objects,which keep associations
between names and objects.For instance,the name “pi” might be associated
with the number 3.14.This stack is used for looking up the meaning of the names
1
￿ ￿ ￿
though not necessarily readable.It is based on PostScript(tm) which often has been called
a Write-only language.Therefore:Be structured,and write comments:).
1
used in the script.It is search from top to bottom.Builtin names (like add and
mul) are placed in a dictionary at the lowest level,which cannot be popped off.
All names,however,can be redefined by the programmer.
The operand stack is the one that explains the “postfix behaviour.” All opera-
tors take their arguments fromthe operator stack and push their results back on
it.So the operator add,for instance,takes two numbers from the operand stack
and pushes back their sum,like this:
1
￿
1
￿
2
￿
2
1
￿
add
￿
3
This is where the most common runtime
2
errors appear:Stack underflow,and
wrong parameter types.If you try to use add and there is only one number on
the stack,you get a stack underflow.If you try to add a string and a list,you’ll
get a wrong parameter type error.
2.1 Object types
There are 10 types of objects in Lace:
1.Numbers
2.Strings
3.Booleans
4.Lists
5.Dictionaries
6.Procedures
7.Names
8.Delimiters
9.Marks
10.Primitives
These are value types —there are no variables,and therefore no variable types.
Numbers are real numbers —there is no separate type for integers.Strings are
simple character strings,written with double quotation marks (").Boolean val-
ues are either true or false.Lists are simple sequences of language objects (possibly
including other lists).They are constructed by the use of primitive delimiter op-
erators (see below).Dictionaries are hashtables containing name objects as keys
and any language objects as values.Procedures are simply executable lists.
2
There are almost no possible compile-time errors,except using letters inside numbers like
3.1w4.
2
Names are objects used to create and refer to definitions.They can either be
written plainly (Like add),slashed (like/add) or doubly slashed (like//add).The
different meanings of these ways of writing themwill be explained below.
Delimiters are also names of sorts;they are single letter names taken from
the set

,

,[,],(,and ).Whereas other names must be separated by whites-
pace,these act as separators themselves.Thus,[add] is really a series of three
names,where the first and last are delimiters.The simple parantheses have no
special meaning,but brackets and braces have primitive definitions (that can be
overridden).A left bracket puts a mark object on the operand stack,and a right
bracket gathers all elements on the operand stack down to the mark object,and
constructs a list object from them.The mark is popped off the stack.The braces
work in a similar way,but the left brace turns off the execution of ordinary names
in addition to putting a mark on the stack.The right brace turns this on again,
as well as constructing a procedure obect.(More about this below.) Note:When
under the influence of braces,both parentheses and brackets are simply pushed
onto the stack like other tokens.The corresponding lists and dictionaries are not
constructed until the surrounding code block is executed.
In addition to these object types,there is one last construct — the comment;
any line in the source code that starts with a percentage sign (%) is ignored.
2.2 Execution
The framework for the execution model has been outlined above.Now we shall
have a look at howa script is really executed.Let’s take the following example:
/square {
dup mul
} def
2 square
The result of this simple script will be 4.The first thing that happens when this is
interpreted,is that an execution frame is created,which consists of the following
series of objects:
/square (Slashed name)

(Delimiter)
dup (Plain name)
mul (Plain name)

(Delimiter)
def (Plain name)
2 (Number object)
square (Plain name)
This execution frame is pushed on the execution stack.The operand stack is
empty,and the dictionary stack holds only the systemdictionary,with the primi-
tive operators defined.
When the execution starts,the first language object is taken fromthe topmost
execution frame.In this case,that is/square,a slashed name.Names are nor-
3
mally executed by fetching their definition from the dictionary stack,but when
they are slashed,this behaviour is suppressed.Slashed names are evaluated sim-
ply by putting their plain counterpart on the operand stack.
The next object — a delimiter — first places a mark object on the operand
stack,and then turns off the normal execution mode for names.This means that
when a plain word is executed,it is treated as if it were slashed.This effect is
nested,so that if more left braces are used,the same number of right braces must
be used to come back to the normal execution mode.(More about this normal
mode later.)
The next two objects,dup and mul,are plain words,but are (because they
are between braces) treated as slashed words,and are simply put on the operand
stack.
The final object in the code block is another delimiter — a right brace.De-
limiters are executed regardless of any procedure marks in effect.The effect of
executing a delimiter is like that of executing a plain name —it is looked up in
the dictionary stack,and its definition is executed.Unless it is overridden,this
definition is a primitive object,which in this case creates a procedure object of all
the objects since the last mark object.
The object def is a plain word,which is executed normally,since the left brace
is no longer in effect.Its definition is found fromthe dictionary stack (in this case
fromthe systemdictionary) and pushed on the execution stack,where it in turn
is executed.The default behaviour of the def operator is to take a plain name
(in this case square) and a procedure (generated by the delimiters) and create a
newdefinition,linkin the two,in the topmost dictionary on the dictionary stack.
So,what we have done up until nowis to define the procedure square.Next
we push the number 2 on the operand stack.Then the word square is encoun-
tered.Since it is not slashed,it is looked up in the dictionary stack,and its defini-
tion (which we just created) is pushed on the execution stack.The next element
to execute will therefore be dup,which duplicates the 2,leaving two number ob-
jects on the operand stack.After this,mul is executed,multiplying the two 2’s
together,leaving 4 on the operand stack.(Note:When the last object in a pro-
cedure is about to be executed,the empty execution frame is popped from the
execution stack,to allowclean tail recursion.)
3 More advanced concepts
There are more advanced data types than numbers available.Lists are created in
a way similar to procedures,except that the execution mode is not altered.So
/n 1 def [n dup mul]
wouldresult ina list with the number 1 init,just as if we hadwritten [1],because
the operators are executed as the list is built.Strings in many ways work like a list
of characters,but the quotation marks are not separate delimiter objects —they
are “hardwired” into the language.
4
3.1 Control structures
Conditional execution is performed with the if and ifelseprimitives.if takes
two arguments;a boolean value and a procedure.If the boolean value is true,
then the procedure is executed.ifelse takes one boolean and two procedures;
the first is executed if the boolean is true,the other if it is false.An example:
% Simple recursive faculty function
/faculty {
dup 0 eq { % If the parameter is zero
pop % then remove the parameter...
1 %...and return one.
}
{ % Otherwise,
dup 1 sub faculty % call self on parameter-1
mul % and multiply the result with the parameter
} ifelse
} def
Loops are created either with the loop operator,or with the for operator.loop
takes a procedure from the operator stack and loops through it indefinitely.To
end the loop,use the break operator,which pops the topmost execution frame
fromthe execution stack.(Note:If breakis used within an ifelse structure or
similar,it will only pop off the surrounding block.To exit more than one block,
make sure all blocks (or the constructs executing them) are the last elements of
the surrounding one.In that case,the surrounding ones will also be popped,as
per the tail recursion mechanismmentioned above.)
Asimple “for”-loop can be implemented with loop:
% Loop from 1 to 10
0 {
1 add
% More code using the number...
dup 10 eq {break} if
} loop
To show some more programming constructs,let’s create an even more com-
plete and general “for”-mechanism(defined as a procedure):
% Takes four arguments:start,increment,stop and the code to
% execute:
/for {
newdict
begin % start a local dictionary
%"pick up"the arguments:
/body exch def
5
/stop exch def
/inc exch def
/start exch def
% Body of loop:
/index start def
{
body % Get the body from surrounding block
/index index inc add def % Increment the index
index stop ge {break} if % Conditional break
} loop
end % end the local dictionary
} def
To understand this example,the operator reference at the end of this docu-
ment may come in handy.(The builtin operator for automates the functionality
shown in this example.)
Some times you might want to use lexical scoping rules — to create one of
the elements of a procedure based on the current environment.That is when you
use the double-slashed names.They are if they are within one level of procedure
nesting,or any level of dictionary nesting with no surrounding procedures.As a
trivial example,consider this function:
/pi 3.14 def
/area {dup mul//pi mul} def
Here,the numeric constant 3.14 is inserted into the procedure instead of the name
pi.This feature can be useful when constructing complex datastructures where
some of the elements are to be filled in from current variables (like messages —
se the section on aglet scripting.)
Though we have shown howdefinitions and code blocks can be used to create
procedure calls and recursion,there is still something missing.Howdo we create
restricted name spaces for each procedure?Howdo we create local “variables”?
In most languages this is an automatic part of the procedure mechanism,but in
Lace (as in PostScript) this namespace mechanismhas been separated into a stack
of its own.By putting a new dictionary onto the dictionary stack,you create a
local name space.By popping it off,you exit into the surrounding one.This
is done by the operators begin,which takes a dictionary as an argument,and
end.The operator newdict creates a newdictionary.Let’s make a newfactorial
function with local variables:
/factorial {
newdict
begin
/n exch def
n 0 eq
{1}
{n 1 sub factorial n mul}
6
ifelse
end
} def
Here the local variable nwill be separate in each recursive call because of the local
dictionary.begin can be used to put any dictionary on the dictionary stack,so
this mechanism is quite flexible.It can be used to edit dictionaries that are not
meant as environments or symbol tables for variables at all.
4 Scripting aglets
Lace is very flexible,and therefore also quite unstructured.To create working
aglets,a fewprogramming idioms may be of good use.
4.1 The structure of an aglet
There are,of course,no limit to the number of ways one may structure the script
of an aglet,but I have found two basic structures to be quite useful.One is the
master aglet and the other is the basic aglet.
The script of a basic aglet is divided into three parts —first comes the decla-
ration of constants,then comes the definition of procedures,and then comes the
definition of a message handler.Note that in general,no code is actually executed
as the script is run.It is merely used to initialize the aglet,to define its behaviour.
The master aglet orchestrates a number of basic aglets.Its script is dividedinto
three parts.First comes the declaration of constants.Then comes the creation
of some other aglets (through the use of newaglet.Finally,messages are sent
to some (or all) of the aglets to activate them.After this,the master aglet does
nothing.Its role is to embody the architecture of an aglet subsystem.It sets up
relationships between the other aglets through message passing.
4.2 Message passing
Lace uses a message passing scheme that makes it possible to send simple KQML-
message that are coded directly in the scripts.Howthis is done will be described
in a second —those who are not interested in the technicalities may skip the last
paragraph of this section.
Messages in Lace are dictionaries.That is —when you send a message,you
send a set of name–value pairs.The dictionary can be created using any of the
methods described in this document,and then sent using the message operator.
However,the dictionary syntax has been constructed so that it closely resembles
that of KQML.Dictionaries are written as a parenthesized list of objects.If the
number of names is even,every pair is entered into the dictionary as a name–
value pair.(Note:Every other object has to be a name!) If the number of objects
is odd,the first is treated as the name of a dictionary.The rest of the list is then
treated as before,but as updates to a copy of the named dictionary.
7
Therefore,to make a KQMLmessage,where the performative is the first word,
you have to define a dictionary constant namedby the performative,andcontain-
ing an entry with the key “:performative” (for instance) and again the name
of the performative as value.Thus,if someone looks up the performative of the
message,it will be equal to the first word.(This is a bit complicated,but it makes
the dictionary mechanism very flexible.You could even base an object oriented
systemon it
￿ ￿ ￿
)
To create a message,you can write somthing like this:
(tell:language list
:sender//this % Double slashes inserts real value
:reply-with id1
:contents [hello world])
And when you do that,even if you care why or not,you have to put a line like
this in the constant definition part of your script:
/tell (:performative tell) def
4.3 Message handling
Amessage handler is put at the endof the basic aglet script.It should be a method
called handle-message.It will be called automatically when the aglet receives
a message,and the message will be on the operand stack when the execution
starts.Astandard way to structure this handler is as a list of “trigger-rules.” You
basically have one rule for each performative (or other part of the message).That
can be done as follows:
/handle-message {
/msg exch def %"pick up"the message
/p msg/:performative get def % define the performative as"p"
p/tell eq {
% Code handling"tell"messages...
} if
p/ask eq {
% Code handling"ask"messages...
} if
p/propose eq {
% Code handling"propose"messages...
} if
% etc...
}
8
5 Debugging
In its current implementation,Lace is quite difficult to debug.The error messages
do not indicate where in the script there is an error,only the line number in the
Java source code (which is of little help if you don’t knowJava in the first place).
I have only three pieces advice to give:
1.Write simple aglets.If you make simple,testable aglets it is easier to find any
errors.If the problemyou want to solve is complex,increase the complexity
of the architecture (and the number of aglets) rather than the size of your
scripts.
2.Use print and print.Print the values of variables at all points around
a suspected bug area.Check if the execution goes where you think it does
by inserting little “hello” printouts at strategic points.(This technique has
done wonders for me in many languages.)
3.Remember the operators.I have often found out that a seeming lack of func-
tionality has been caused by the lack of a corresponding operator —that is,
I have created an “if” sentence without the “if” or a “for” loop without the
“for”.Since these are usually put at the topof code blocks in other language,
it is easy to forget.(At least I think so
￿ ￿ ￿
)
6 Operator reference
Each operator in the following is described with a single line,like this:
arg arg operator result
The first series of words represents the top of the stack,that is,the arguments
that the operator needs.Next comes the name of the operator in boldface,and
finally the top of the stack after the operator is executed,that is,its return values.
6.1 Mathematical operators
num1 num2 add sum
Adds two numbers and returns the sum.
num1 num2 sub result
Subtracts num2 fromnum1 and returns the result.
num1 num2 mul product
Multiplies two numbers and returns the product.
num neg
￿
num
Negates a number.
9
num1 round num2
Rounds off a number to an integer value.
num1 num2 div quotient
Divides num1 by num2 and returns the quotient.
num1 num2 idiv result
Integer division.Equivalent to dividing num1 by num2 and rounding
the result.
num1 num2 mod result
Modulus.The num1 is equivalent to the (minimal) result,modulo
num2;that is —dividing num1 by num2,the remainder is returned
by mod.
6.2 Stack operators
any1 any2 exch any2 any1
Exchange the two top elements of the stack.
any pop —
Remove the top element of the stack.
any dup any any
Duplicate the top element of the stack,leaving both copies in place.
(This is a copy of a reference,that is,the two copies are identical;they
are two references to the same object.)
￿ ￿ ￿ ￿
anyn clear
￿
Removes all elements fromthe operand stack.
any
1
￿ ￿ ￿
any
n
num copy any
1
￿ ￿ ￿
any
n
any
1
￿ ￿ ￿
any
n
Copies the n topmost elements,excluding num (where n is given by
num).
any
n
￿ ￿ ￿
any
0
num index any
n
￿ ￿ ￿
any
0
Copies element no.“num” fromthe top and pushes it onto the stack.
“0 index” is equivalent to “dup” or “1 copy”.
any
n
any
n
￿
1
￿ ￿ ￿
any
0
n j roll any
￿
j
￿
1
￿
mod n
￿ ￿ ￿
any
j mod n
Rotates the n top element j places,so that the jth element (modulo n)
ends up on top.
10
6.3 List and string operators
Most of these operators treat strings as if they were lists.
list index get any
Gets the element from the first element given by the number in the
second element.For example,["a""b""c"] 2 get returns"b”.
list index count getinterval sublist
Get the part of the list indicated by the index and the count.For exam-
ple,["a""b""c""d"] 2 3 getinterval would return ["b"
"c""d"].[Not implemented.]
list index value put —
The “opposite” of get.Puts the value into the list at the position indi-
cated by the index.
list length num
Returns the length of a string or a list.
list object member bool
Checks whether or not an object is a member of a list.(Can be used
in conjunction with forall and logical operators to create set operators
for lists.)
list object append list
Appends the object to the end of the list.If the object is itself a list,the
result will be the concatenation of the two —otherwise,the object is
simply added as a single element.
6.4 Dictionary operators
— newdict dict
Returns a newly created dictionary.
dict begin —
Starts using the dictionary on the operand stack (by placing it on the
dictionary stack.)
— end —
Stop using the current dictionary (by popping it off the dictionary
stack.)
key value def —
Insert a definition into the current dictionary (the one on top of the
dictionary stack),consisting of the name “key” andthe arbitrary object
“value”.
11
dict key get any
Similar to get usedon a list or a string.Gets the value that corresponds
to the given key in the given dictionary.
dict key value put —
As with put —enters the key–value pair as a definition in the given
dictionary.(Only names are allowed as keys.)
dict clone dict dict
Create a clone of a dictionary.Ordinarily,if you change the contents
of a dictionary,all other copies of it (that is,references to it) will also
change.Cloneddictionaries canbe alteredindependentlyof eachother.
— currentdict dict
Returns the dictionary currently in use (i.e.the one on top of the dic-
tionary stack).
6.5 Relational operators
any1 any2 eq bool
Returns true if the two objects are identical,and false otherwise.
any1 any2 ne bool
Returns true if the two objects are different,and false otherwise.
num1 num2 ge bool
Returns true if num1 is greater than or equal to num2,and false other-
wise.
num1 num2 gt bool
Returns true if num1 is greater than num2,and false otherwise.
num1 num2 lt bool
Returns true if num1 is less than num2,and false otherwise.
num1 num2 le bool
Returns true if num1 is less than,or equal to num2,and false other-
wise.
6.6 Logical operators
— true bool
Returns true.
— false bool
Returns false.
12
bool1 bool2 and bool
Returns the conjunction of two boolean values.
bool1 bool2 or bool
Returns the disjunction of two boolean values.
bool not bool
Returns the logical negation of a boolean value.
6.7 Control operators

proc

exec —
Executes a block of code (by putting it onto the execution stack).

proc

loop —
Repeatedly(andindefinitely) executes the block of code.Canbe stopped
by a call to exit.
num

proc

repeat —
Repeatedly executes the block of code numtimes.
init incr max

proc

for —
Similar to the usual way of using a for-loop in languages like C.The
block of code is executed repeatedly,with each iteration starting with
an index value on the operand stack.For the first iteration,this index
equals init.Thereafter it is incremented by incr before each new iter-
ation.The loop stops immediately (that is,before entering the code-
block) when the incremented index is greater than max.If the interval
between init and max is equally divisible by incr,then max will be the
last value of the index.(Also works with max less than init,with a
negative incr.) Note:The code block is responsible for dealing with
the index.If it is left on the stack,it may result in a large buildup if the
loop is repeated many times.
list

proc

forall —
Repeatedly executes the code block,at the beginning of each iteration
placing successive elements of the list on the operand stack.For in-
stance,0 [1 2 3 4 5]

add

forall would return 15.
dict

proc

forall —
Works similarly to when used on lists.Instead of successive elements
(as in a list),key-value–pairs are placed on the operand stack.The
order in which they appear is unspecified,but each definition in the
dictionary will be used exactly once,and then the loop will terminate.
bool

proc

if —
The code block is executed if (and only if) the boolean value is true.
13
bool

proc1
 
proc2

ifelse —
If the boolean value is true,the first code block is executed —other-
wise,the second is executed.
— exit —
Pops the current execution frame (code block) fromthe execution frame.
That is —it exits the current code block.May be used to exit loop con-
structs (to create something like a while-loop) like this:
/iter 0 def
{
iter 10 eq {exit} if
/iter iter 1 add def
} loop
As was explained earlier,to enable clean tail recursion,empty (fin-
ished) code blocks are removed from the execution stack before their
last object is executed.Therefore,exit here exits the loop-block,and
not its own block (which is already gone).
6.8 Aglet operators
These operators are not part of the basic language,but have beenaddedfor script-
ing aglets.The functionality available is quite limited,and is primarily intended
for experimentation with agent concepts.There are also some utility operators
for input and output,not directly related to the aglet functionality.
In addition to the newoperators,a newlanguage element is introduced —the
aglet.This way,aglets are handled direcly by the operators (instead of through
names or something similar.)
string dispatch —
Move to the adress given by the string which is an atp-URL (Agent
Transfer Protocol).For instance"atp://localhost:9000".
aglet dict message —
Sends a message to the given aglet.The message must be a dictio-
nary without any procedure object (due to security reasons).Due to
the parenthesis syntax of dictionaries,these may be written almost
like KQML-messages.A difference is that aglets are not available by
name,so sender and receiver etc.cannot be written out directly.This
is a place where double-slashed names are handy.By using them,ref-
erences to aglets can be inserted directly into the dictionery.For ex-
ample,using the fictitious performative “order,” one aglet may order
another to befriend a third:
% Explained later:
/order (:performative order) def
some-aglet (order:command befriend
:target//some-other-aglet) message
14
The first line is used to enable the use of the performative as a simple
first argument in the message.Without it,the script would look like
this:
some-aglet (:performative order
:command befriend
:target//some-other-aglet) message
There is of course no need to use this KQML-like syntax if it is not
desired.[Security check not implemented.]
— aglets list
Returns a list of all the active aglets present at the current server.This
is useful for broadcasting messages.For example,to broadcast an alert
to everyone present,one might write aglets

alert-msg message

forall.
It is not possible to get this kind of information fromanother server
direcly,so to get it an aglet would either have to go there itself,or
create some slave aglet that would go there to fetch it.
string newaglet aglet
Creates a script-aglet based on the script whose location is given by
the URL in string.
string print —
Prints the string to standard output (usually the command window
fromwhich the aglet server was started).
num deactivate —
Deactivates the aglet for a given number of seconds.Can be used to
create clocking aglets or “reminder” aglets.The aglet that wants to be
reminded can simply create another aglet that will sleep until its time.
— this aglet
Returns the current aglet.
object string putenv —
Puts the object into the environment property (in the current server)
named by the given string.Can be used as a name registry,for in-
stance:A manager aglet may be registered under the string “man-
ager,” and visiting aglets can then easily access the manager by look-
ing up the environment property.
— this aglet
Returns the current aglet.
string println —
Prints the string to standard output with a newline appended.
— readln string —
Reads a line fromstandard input.
15