20.1 Barrier
Barrier is used to wait until another job raises a signal. This can be used to implement blocking calls
waiting until a resource is available.
A Barrier can be created with no argument. Signals and wait calls done on this instance are restricted
to this instance.
Barrier.new;
[00000000] Barrier_0x25d2280
- signal(payload)
Wake up one of the job waiting for a signal. The payload is sent to the wait method. This
method returns the number of job woken up.
do (Barrier.new)
{
echo(wait) &
echo(wait) &
assert
{
signal(1) == 1;
signal(2) == 1;
}
}|;
[00000000] *** 1
[00000000] *** 2
- signalAll(payload)
Wake up all the jobs waiting for a signal. The payload is sent to all wait methods. Return the
number of jobs woken up.
do (Barrier.new)
{
echo(wait) &
echo(wait) &
assert
{
signalAll(1) == 2;
signalAll(2) == 0;
}
}|;
[00000000] *** 1
[00000000] *** 1
- wait
Block until a signal is received. The payload sent with the signal function is returned by the
wait method.
do (Barrier.new)
{
echo(wait) &
signal(1)
}|;
[00000000] *** 1
20.2 Binary
A Binary object, sometimes called a blob, is raw memory, decorated with a user defined
header.
Binaries are usually not made by users, but they are heavily used by the internal machinery when
exchanging Binary UValues. A binary features some content and some keywords, both simple
Strings (??sec:std-String).
Binary.new("my header"
, "my content"
);
[00000001] BIN 10 my header
my content
Beware that the third line above (‘my content’), was output by the system, although not preceded
by a timestamp.
20.3 Boolean
There is no object Boolean in urbiscript, but two specific objects true and false. They are the
result of all the comparison statements.
The objects true and false have the following prototype.
There are no constructors, use true and false. Since they are singletons, clone will return
themselves, not new copies.
true;
!false;
2 < 6 === true;
true.new === true;
6 < 2 === false;
As in many programming languages, conditions may be more than only true and false. Whether
some value is considered as true depends on the type of this. Actually, by default objects as
considered “true”, objects evaluating to “false” are the exception:
The method Object.asBool is in charge of converting some arbitrary value into a Boolean.
assert(Global.asBool == true);
assert(nil.asBool == false);
void.asBool;
[00000421:error] !!! unexpected void
- ’&&’(that)
Short-circuiting logical and. If this is true evaluate and return that. If this is false, return
itself without evaluating that.
(true && 2) == 2;
(false && 1 / 0) == false;
- ’||’(that)
Short-circuiting logical or. If this is false evaluate and return that. If this is true, return
itself without evaluating that.
(true || 1/0) == true;
(false || 2) == 2;
- ’!’
Logical negation. If this is false return true and vice-versa.
!true == false;
!false == true;
- asBool
Identity.
true.asBool == true;
false.asBool == false;
20.4 CallMessage
Capturing a method invocation: its target and arguments.
The following example implements a lazy function which takes an integer n, then arguments. The n-th
argument is evaluated twice using evalArgAt.
function callTwice
{
var n = call.evalArgAt(0);
call.evalArgAt(n);
call.evalArgAt(n)
} |;
callTwice(1, echo("foo"
), echo("bar"
));
[00000001] *** foo
[00000002] *** foo
callTwice(2, echo("foo"
), echo("bar"
));
[00000003] *** bar
[00000004] *** bar
Strict functions do support call.
function strict(x)
{
echo("Entering"
);
echo("Strict: "
+ x);
echo("Lazy: "
+ call.evalArgAt(0));
} |;
strict({echo("1"
); 1});
[00000011] *** 1
[00000013] *** Entering
[00000012] *** Strict: 1
[00000013] *** 1
[00000014] *** Lazy: 1
- args
The list of unevaluated arguments.
function args { call.args }|
assert
{
args == [];
args() == [];
args({echo(111); 1}) == [Lazy.new(closure() {echo(111); 1})];
args(1, 2) == [Lazy.new(closure () {1}),
Lazy.new(closure () {2})];
};
- argsCount
The number of arguments. Do not evaluate them.
function argsCount { call.argsCount }|;
assert
{
argsCount == 0;
argsCount() == 0;
argsCount({echo(1); 1}) == 1;
argsCount({echo(1); 1}, {echo(2); 2}) == 2;
};
- code
The body of the called function as a Code (??sec:std-Code).
function code { call.getSlot("code"
) }|
assert (code == getSlot("code"
));
- evalArgAt(n)
Evaluate the n-th argument, and return its value. n must evaluate to an non-negative integer.
Repeated invocations repeat the evaluation, see Section 20.4.1.1.
function sumTwice
{
var n = call.evalArgAt(0);
call.evalArgAt(n) + call.evalArgAt(n)
}|;
function one () { echo("one"
); 1 }|;
sumTwice(1, one, one + one);
[00000008] *** one
[00000009] *** one
[00000010] 2
sumTwice(2, one, one + one);
[00000011] *** one
[00000012] *** one
[00000011] *** one
[00000012] *** one
[00000013] 4
sumTwice(3, one, one);
[00000014:error] !!! evalArgAt: invalid index: 3
sumTwice(3.14, one, one);
[00000015:error] !!! evalArgAt: invalid index: 3.14
- evalArgs
Call evalArgAt for each argument, return the list of values.
function twice
{
call.evalArgs + call.evalArgs
}|;
twice({echo(1); 1}, {echo(2); 2});
[00000011] *** 1
[00000012] *** 2
[00000011] *** 1
[00000012] *** 2
[00000013] [1, 2, 1, 2]
- message
The name under which the function was called.
function myself { call.message }|
assert(myself == "myself"
);
- sender
The object from which the invocation was made (the caller in other languages). Not to be
confused with target.
function Global.getSender { call.sender } |
function Global.callGetSender { getSender } |
assert
{
getSender === lobby;
Global.getSender === lobby;
callGetSender === lobby;
Global.callGetSender === Global;
};
- target
The object on which the invocation is made. In other words, the object that will be bound to
this during the evaluation. Not to be confused with sender.
function Global.getTarget { call.target } |
function Global.callGetTarget { getTarget } |
assert
{
getTarget === lobby;
Global.getTarget === Global;
callGetTarget === lobby;
Global.callGetTarget === Global;
};
20.5 Channel
Returning data, typically asynchronous, with a label so that the “caller” can find it in the
flow.
Channels are created like any other object. The constructor must be called with a string which will be
the label.
var ch1 = Channel.new("my_label"
);
[00000201] Channel_0x7985810
ch1 << 1;
[00000201:my_label] 1
var ch2 = ch1;
[00000201] Channel_0x7985810
ch2 << 1/2;
[00000201:my_label] 0.5
20.6 Code
Functions written in urbiscript.
The keywords function and closure build Code instances.
function(){}.protos[0] === getSlot("Code"
);
closure(){}.protos[0] === getSlot("Code"
);
- Whether this and that are the same source code (actually checks that both have the
same asString), and same closed values.
Closures and functions are different, even if the body is the same.
function () { 1 } == function () { 1 };
function () { 1 } != closure () { 1 };
closure () { 1 } != function () { 1 };
closure () { 1 } == closure () { 1 };
No form of equivalence is applied on the body, it must be the same.
function () { 1 + 1 } == function () { 1 + 1 };
function () { 1 + 2 } != function () { 2 + 1 };
Arguments do matter, even if in practice the functions are the same.
function (var ignored) {} != function () {};
function (var x) { x } != function (y) { y };
A lazy function cannot be equal to a strict one.
function () { 1 } != function { 1 };
If the functions capture different variables, they are different.
{
var x;
function Global.capture_x() { x };
function Global.capture_x_again () { x };
{
var x;
function Global.capture_another_x() { x };
}
}|;
assert
{
getSlot("capture_x"
) == getSlot("capture_x_again"
);
getSlot("capture_x"
) != getSlot("capture_another_x"
);
};
If the functions capture different targets, they are different.
class Foo
{
function makeFunction() { function () {} };
function makeClosure() { closure () {} };
}|;
class Bar
{
function makeFunction() { function () {} };
function makeClosure() { closure () {} };
}|;
assert
{
Foo.makeFunction() == Bar.makeFunction();
Foo.makeClosure() != Bar.makeClosure();
};
- apply(args)
Invoke the routine, with all the arguments. The target, this, will be set to args[0] and the
remaining arguments with be given as arguments.
function (x, y) { x+y }.apply([nil, 10, 20]) == 30;
function () { this }.apply([123]) == 123;
1.apply([this]) == 1;
function () {}.apply([]);
[00000001:error] !!! apply: argument list must begin with ‘this’
function () {}.apply([1, 2]);
[00000002:error] !!! apply: expected 0 argument, given 1
- asString
Conversion to String (??sec:std-String).
closure () { 1 }.asString == "closure () {\n 1\n}";
function () { 1 }.asString == "function () {\n 1\n}";
- bodyString
Conversion to String (??sec:std-String) of the routine body.
closure () { 1 }.bodyString == "1"
;
function () { 1 }.bodyString == "1"
;
20.7 Comparable
Objects that can be compared for equality and inequality. See also Orderable (??sec:std-Orderable).
This object, made to serve as prototype, provides a definition of != based on ==. Object provides a
default implementation of == that bounces on the physical equality ===.
class Foo : Comparable
{
var value = 0;
function init (v) { value = v; };
function ’==’ (lhs) { value == lhs.value; };
};
[00000000] Foo
Foo.new(1) == Foo.new(1);
[00000000] true
Foo.new(1) == Foo.new(2);
[00000000] false
- Whether ! (this != that).
class FortyTwo : Comparable
{
function ’!=’ (that) { 42 != that };
}|;
assert
{
FortyTwo != 51;
FortyTwo == 42;
};
- !=(that)
Whether ! (this == that).
class FiftyOne : Comparable
{
function ’==’ (that) { 51 == that };
}|;
assert
{
FiftyOne == 51;
FiftyOne != 42;
};
20.8 Container
This object is meant to be used as a prototype for objects that support has and hasNot methods.
Any class using this prototype must redefine either has, hasNot or both.
- has(e)
!hasNot(e). The indented semantics is “true when the container has a key (or item)
matching e”. This is what e in c is mapped onto.
class NotCell : Container
{
var val;
function init(var v) { val = v };
function hasNot(var v) { val != v };
}|;
var c = NotCell.new(23)|;
assert
{
c.has(23); 23 in c;
c.hasNot(3); 3 not in c;
};
- hasNot(e)
!has(e). The indented semantics is “true when the container does not have a key (or item)
matching e”.
class Cell : Container
{
var val;
function init(var v) { val = v };
function has(var v) { val == v };
}|;
var d = Cell.new(23)|;
assert
{
d.has(23); 23 in d;
d.hasNot(3); 3 not in d;
};
20.9 Control
Control is designed as a namespace for control sequences. Some of these entities are used by the
Urbi engine to execute some urbiscript features; in other words, users are not expected to you use it,
much less change it.
20.10 Date
This class is meant to record dates in time.
This feature is experimental. It might be changed in the future. Feedback on its use
would be appreciated.
Without argument, newly constructed dates refer to the epoch (see epoch).
Date.new;
[00000001] 1970-01-01 01:00:00
With a numeric argument s, refers to the date that is s seconds after the epoch.
Date.new(1234567890);
[00023593] 2009-02-14 00:31:30
With a string argument d, refers to the date contained in d. The string should be formatted as
‘yyyy-mm-dd hh:mn:ss ’ (see asString). mn and ss are optional. If the block ‘hh:mn:ss ’ is absent, the
behavior is undefined.
Date.new("2003-10-10 20:10:50"
);
[00086457] 2003-10-10 20:10:50
Date.new("2003-10-10 20:10"
);
[00091439] 2003-10-10 20:10:00
Date.new("2003-10-10 20"
);
[00094386] 2003-10-10 20:00:00
If you create a date with a numeric argument which cannot be interpreted as a date
you should get an error message warning you that the value cannot be converted into a
integer
Date.new(0.1);
[00095042:error] !!! new: bad numeric conversion: overflow or non empty fractional part: 0.1
- ’+’(that)
Return the date which is corresponds to waiting Duration (??sec:std-Duration) that after this.
Date.new(1000) + Duration.new(100) == Date.new(1100);
Date.new(1000) + Duration.new(-100) == Date.new(900);
- ’-’(that)
Compute the difference between two dates. Return a Duration (??sec:std-Duration).
Date.new(100) - Date.new(0) == Duration.new(100);
Date.new(0) - Date.new(100) == Duration.new(-100);
- ’==’(that)
Equality test.
Date.new(11223344) == Date.new(11223344);
Date.new(11111111) != Date.new(22222222);
- ’<’(that)
Order comparison.
Date.new(11111111) < Date.new(22222222);
- asFloat
Give the numeric value of the date that refers to the date that is s seconds after the epoch.
Date.new("2002-01-20 23:59:59"
).asFloat == 1011567599;
- asString
Present as ‘yyyy-mm-dd hh:mn:ss ’ where yyyy is the four-digit year, mm the two-digit month
(from 1 to 12), dd the two-digit day in the month (from 1 to 31), hh the two-digit hour (from 0 to
23), mn the two-digit number of minutes in the hour (from 0 to 59), and ss the two-digit number
of seconds in the minute (from 0 to 59).
Date.new(1234567890).asString == "2009-02-14 00:31:30"
;
- epoch
A fixed value, the “origin of times”: January 1st 1970, at midnight.
Date.epoch == Date;
Date.epoch == Date.new(0);
Date.epoch == Date.new();
- now
The current date.
Date.now;
[00000000] 2012-03-02 15:31:42
- timestamp
Synonym for asFloat
20.11 Dictionary
A dictionary is an associative array, also known as a hash in some programming languages. They
are arrays whose indexes are strings.
In a way objects are dictionaries: one can use setSlot, updateSlot, and getSlot.
This is unsafe since slots also contains value and methods that object depend upon to run
properly.
The following session demonstrates the features of the Dictionary objects.
var d = ["one"
=> 1, "two"
=> 2];
[00000001] ["one" => 1, "two" => 2]
for (var p : d)
echo (p.first + " => "
+ p.second);
[00000003] *** one => 1
[00000002] *** two => 2
"three"
in d;
[00000004] false
d["three"
];
[00000005:error] !!! missing key: three
d["three"
] = d["one"
] + d["two"
] | {};
"three"
in d;
[00000006] true
d.getWithDefault("four"
, 4);
[00000007] 4
The Dictionary constructor takes arguments by pair (key, value).
Dictionary.new("one"
, 1, "two"
, 2);
[00000000] ["one" => 1, "two" => 2]
Dictionary.new;
[00000000] [ => ]
Yet you are encouraged to use the specific syntax for Dictionary literals:
["one"
=> 1, "two"
=> 2];
[00000000] ["one" => 1, "two" => 2]
[=>];
[00000000] [ => ]
An extra comma can be added at the end of the list.
[
"one"
=> 1,
"two"
=> 2,
];
[00000000] ["one" => 1, "two" => 2]
- ’==’(that)
Whether this equals that. This suppose that elements contained inside the dictionary are
Comparable.
[ => ] == [ => ];
["a"
=> 1, "b"
=> 2] == ["b"
=> 2, "a"
=> 1];
- ’[]’(key)
Syntactic sugar for get(key).
["one"
=> 1]["one"
] == 1;
- ’[]=’(key, value)
Syntactic sugar for set(key, value), but returns value.
{
var d = ["one"
=>"2"
];
assert
{
(d["one"
] = 1) == 1;
d["one"
] == 1;
};
};
- asBool
Negation of empty.
[=>].asBool == false;
["key"
=> "value"
].asBool == true;
- asList
Return the contents of the dictionary as a Pair (??sec:std-Pair) list (key, value).
["one"
=> 1, "two"
=> 2].asList == [("one"
, 1), ("two"
, 2)];
Since Dictionary derives from RangeIterable (??sec:std-RangeIterable), it is easy to iterate over
a Dictionary using a range-for (Section 19.6.5.2). No particular order is ensured.
{
var res = [];
for| (var entry: ["one"
=> 1, "two"
=> 2])
res << entry.second;
assert(res == [1, 2]);
};
- asString
A string representing the dictionary. There is no guarantee on the order of the output.
[=>].asString == "[ => ]"
;
["a"
=> 1, "b"
=> 2].asString == "[\"a\" => 1, \"b\" => 2]"
;
- clear
Empty the dictionary.
["one"
=> 1].clear.empty;
- empty
Whether the dictionary is empty.
[=>].empty == true;
["key"
=> "value"
].empty == false;
- erase(key)
Remove the mapping for key.
["one"
=> 1, "two"
=> 2].erase("two"
) == ["one"
=> 1]
- get(key)
Return the value associated to key. A Dictionary.KeyError exception is thrown if the key is
missing.
assert(["one"
=> 1, "two"
=> 2].get("one"
) == 1);
try
{
["one"
=> 1, "two"
=> 2].get("three"
);
echo("never reached"
);
}
catch (var e if e.isA(Dictionary.KeyError))
{
assert(e.key == "three"
)
};
- getWithDefault(key, defaultValue)
The value associated to key if it exists, defaultValue otherwise.
do (["one"
=> 1, "two"
=> 2])
{
assert
{
getWithDefault("one"
, -1) == 1;
getWithDefault("three"
, 3) == 3;
};
}|;
- has(key)
Whether the dictionary has a mapping for key.
do (["one"
=> 1])
{
assert(has("one"
));
assert(!has("zero"
));
}|;
The infix operators in and not in use has (see Section 19.1.8.6).
"one"
in ["one"
=> 1];
"two"
not in ["one"
=> 1];
- init(key1, value1, ...)
Insert the mapping from key1 to value1 and so forth.
Dictionary.clone.init("one"
, 1, "two"
, 2);
[00000000] ["one" => 1, "two" => 2]
- keys
The list of all the keys. No particular order is ensured. Since List (??sec:std-List)
features the same function, uniform iteration over a List or a Dictionary is possible.
{
var d = ["one"
=> 1, "two"
=> 2];
assert(d.keys == ["one"
, "two"
]);
assert({
var res = [];
for (var k: d.keys)
res << d[k];
res
}
== [1, 2]);
};
- matchAgainst(handler, pattern)
Pattern matching on members. See Pattern (??sec:std-Pattern).
{
["a"
=> var a] = ["a"
=> 1, "b"
=> 2];
assert(a == 1);
};
- set(key, value)
Map key to value and return this so that invocations to set can be chained. The possibly
existing previous mapping is overridden.
[=>].set("one"
, 2).set("one"
, 1);
[00000000] ["one" => 1]
- size
Number of element in the dictionary.
{
var d = [=>];
assert(d.size == 0);
d["a"
] = 0;
assert(d.size == 1);
d["b"
] = 1;
assert(d.size == 2);
d["a"
] = 2;
assert(d.size == 2);
};
20.12 Directory
A Directory represents a directory of the file system.
A Directory can be constructed with one argument: the path of the directory using a String
(??sec:std-String) or a Path (??sec:std-Path). It can also be constructed by the method open of Path
(??sec:std-Path).
Directory.new("."
);
[00000001] Directory(".")
Directory.new(Path.new("."
));
[00000002] Directory(".")
- asList
The contents of the directory as a Path (??sec:std-Path) list. The various paths include
the name of the directory this.
- asString
A String (??sec:std-String) containing the path of the directory.
Directory.new("."
).asString == "."
;
- content
The contents of the directory as a String (??sec:std-String) list. The strings include only the
last component name; they do not contain the directory name of this.
- fileCreated(name)
Event launched when a file is created inside the directory. May not exist if not supported by your
architecture.
if (Path.new("./dummy.txt"
).exists)
File.new("./dummy.txt"
).remove;
{
var d = Directory.new("."
);
waituntil(d.fileCreated?(var name));
assert
{
name == "dummy.txt"
;
Path.new(d.asString + "/"
+ name).exists;
};
}
&
{
sleep(100ms);
File.create("./dummy.txt"
);
}|;
- fileDeleted(name)
Event launched when a file is deleted from the directory. May not exist if not supported by your
architecture.
if (!Path.new("./dummy.txt"
).exists)
File.create("./dummy.txt"
)|;
{
var d = Directory.new("."
);
waituntil(d.fileDeleted?(var name));
assert
{
name == "dummy.txt"
;
!Path.new(d.asString + "/"
+ name).exists;
};
}
&
{
sleep(100ms);
File.new("./dummy.txt"
).remove;
}|;
20.13 Duration
This class records differences between Dates (??sec:std-Date).
This feature is experimental. It might be changed in the future. Feedback on its use
would be appreciated.
Without argument, a null duration.
Duration.new;
[00000001] Duration(0s)
Duration.new(1h);
[00023593] Duration(3600s)
Durations can be negative.
Duration.new(-1);
[00000001] Duration(-1s)
20.14 Event
An event can be “emitted” and “caught”, or “sent” and “received”. See also Section 12.2.
There are several examples of uses of events in the documentation of event-based constructs.
See at (Section 19.9.1), waituntil (Section 19.9.5), whenever (Section 19.9.6), and so
forth. The tutorial chapter about event-based programming contains other examples, see
Chapter 12.
An Event is created like any other object, without arguments.
var e = Event.new;
[00000001] Event_0x9ad8118
20.15 Exception
Exceptions are used to handle errors. More generally, they are a means to escape from the normal
control-flow to handle exceptional situations.
The language support for throwing and catching exceptions (using try/catch and throw, see
Section 19.7) work perfectly well with any kind of object, yet it is a good idea to throw only objects
that derive from Exception.
There are several types of exceptions, each of which corresponding to a particular kind of error. The
top-level object, Exception, takes a single argument: an error message.
Exception.new("something bad has happened!"
);
[00000001] Exception ‘something bad has happened!’
Exception.Arity.new("myRoutine"
, 1, 10, 23);
[00000002] Exception.Arity ‘myRoutine: expected between 10 and 23 arguments, given 1’
Exception has many slots which are specific exceptions. See Section 20.15.4 for their documentation.
- backtrace
The call stack at the moment the exception was thrown (not created), as a List (??sec:std-List)
of StackFrames (??sec:std-StackFrame), from the innermost to the outermost call.
try
{
function innermost () { throw Exception.new("Ouch"
) };
function inner () { innermost() };
function outer () { inner() };
function outermost () { outer() };
outermost();
}
catch (var e)
{
assert
{
e.backtrace[0].location.asString == "file.u:4.27-37"
;
e.backtrace[0].name == "innermost"
;
e.backtrace[1].location.asString == "file.u:5.27-33"
;
e.backtrace[1].name == "inner"
;
e.backtrace[2].location.asString == "file.u:6.27-33"
;
e.backtrace[2].name == "outer"
;
e.backtrace[3].location.asString == "file.u:8.3-13"
;
e.backtrace[3].name == "outermost"
;
};
};
- location
The location from which the exception was thrown (not created).
eval("1/0"
);
[00090441:error] !!! 1.1-3: /: division by 0
try
{
eval("1/0"
);
}
catch (var e)
{
assert (e.location.asString == "1.1-3"
);
};
- message
The error message provided at construction.
Exception.new("Ouch"
).message == "Ouch"
;
- ArgumentType.new(routine, index, effective, expected)
Derives from Type. The routine was called with a index-nth argument of type effective
instead of expected.
Exception.ArgumentType.new("myRoutine"
, 1, "hisResult"
, "myExceptation"
);
[00000003] Exception.ArgumentType ‘myRoutine: unexpected "hisResult" for argument 1, expected a String’
- Arity.new(routine, effective, min, max = void)
The routine was called with an incorrect number of arguments (effective). It requires at least
min arguments, and, if specified, at most max.
Exception.Arity.new("myRoutine"
, 1, 10, 23);
[00000004] Exception.Arity ‘myRoutine: expected between 10 and 23 arguments, given 1’
- BadInteger.new(routine, fmt, effective)
The routine was called with an inappropriate integer (effective). Use the format fmt to create
an error message from effective. Derives from BadNumber.
Exception.BadInteger.new("myRoutine"
, "bad integer: %s"
, 12);
[00000005] Exception.BadInteger ‘myRoutine: bad integer: 12’
- BadNumber.new(routine, fmt, effective)
The routine was called with an inappropriate number (effective). Use the format fmt to
create an error message from effective.
Exception.BadNumber.new("myRoutine"
, "bad number: %s"
, 12.34);
[00000005] Exception.BadNumber ‘myRoutine: bad number: 12.34’
- Constness.new(msg)
An attempt was made to change a constant value.
Exception.Constness.new;
[00000006] Exception.Constness ‘cannot modify const slot’
- FileNotFound.new(name)
The file named name cannot be found.
Exception.FileNotFound.new("foo"
);
[00000007] Exception.FileNotFound ‘file not found: foo’
- ImplicitTagComponent.new(msg)
An attempt was made to create an implicit tag, a component of which being undefined.
Exception.ImplicitTagComponent.new;
[00000008] Exception.ImplicitTagComponent ‘invalid component in implicit tag’
- Lookup.new(object, name)
A failed name lookup was performed om object to find a slot named name. If
Exception.Lookup.fixSpelling is true (which is the default), suggest what the user might have
meant to use.
Exception.Lookup.new(Object, "GetSlot"
);
[00000009] Exception.Lookup ‘lookup failed: Object’
- MatchFailure.new
A pattern matching failed.
Exception.MatchFailure.new;
[00000010] Exception.MatchFailure ‘pattern did not match’
- NegativeNumber.new(routine, effective)
The routine was called with a negative number (effective). Derives from BadNumber.
Exception.NegativeNumber.new("myRoutine"
, -12);
[00000005] Exception.NegativeNumber ‘myRoutine: expected non-negative number, got -12’
- NonPositiveNumber.new(routine, effective)
The routine was called with a non-positive number (effective). Derives from BadNumber.
Exception.NonPositiveNumber.new("myRoutine"
, -12);
[00000005] Exception.NonPositiveNumber ‘myRoutine: expected positive number, got -12’
- Primitive.new(routine, msg)
The built-in routine encountered an error described by msg.
Exception.Primitive.new("myRoutine"
, "cannot do that"
);
[00000011] Exception.Primitive ‘myRoutine: cannot do that’
- Redefinition.new(name)
An attempt was made to refine a slot named name.
Exception.Redefinition.new("foo"
);
[00000012] Exception.Redefinition ‘slot redefinition: foo’
- Scheduling.new(msg)
Something really bad has happened with the Urbi task scheduler.
Exception.Scheduling.new("cannot schedule"
);
[00000013] Exception.Scheduling ‘cannot schedule’
- Syntax.new(loc, message, input)
Declare a syntax error in input, at location loc, described by message. loc is the location of the
syntax error, location is the place the error was thrown. They are usually equal, except when
the errors are caught while using System.eval or System.load. In that case loc is really the
position of the syntax error, while location refers to the location of the System.eval or
System.load invocation.
Exception.Syntax.new(Location.new(Position.new("file.u"
, 14, 25)),
"unexpected pouCharque"
, "file.u"
);
[00000013] Exception.Syntax ‘syntax error: file.u:14.25: unexpected pouCharque’
try
{
eval("1 / / 0"
);
}
catch (var e)
{
assert
{
e.isA(Exception.Syntax);
e.loc.asString == "1.5"
;
e.input == "1 / / 0"
;
e.message == "unexpected /"
;
}
};
- Type.new(effective, expected)
A value of type effective was received, while a value of type expected was expected.
Exception.Type.new("hisResult"
, "myExceptation"
);
[00000014] Exception.Type ‘unexpected "hisResult", expected a String’
- UnexpectedVoid.new
An attempt was made to read the value of void.
Exception.UnexpectedVoid.new;
[00000015] Exception.UnexpectedVoid ‘unexpected void’
var a = void;
a;
[00000016:error] !!! unexpected void
[00000017:error] !!! lookup failed: a
20.16 Executable
This class is used only as a common ancestor to Primitive (??sec:std-Primitive) and Code
(??sec:std-Code).
There is no point in constructing an Executable.
- asExecutable
Return this.
20.17 File
Files may be created from a String (??sec:std-String), or from a Path (??sec:std-Path). The file must
exist on the file system, and must be a file. You may use create to create a file that does not exist (or
to override an existing one).
System.system("(echo 1; echo 2) >file.txt"
)|;
File.new("file.txt"
);
[00000001] File("file.txt")
File.new(Path.new("file.txt"
));
[00000001] File("file.txt")
You may use InputStream (??sec:std-InputStream) and OutputStream (??sec:std-OutputStream)
to read or write to Files.
- asList
Read the file, and return its content as a list of its lines.
System.system("(echo 1; echo 2) >file.txt"
)|;
assert(File.new("file.txt"
).asList == ["1"
, "2"
]);
- asPrintable
System.system("(echo 1; echo 2) >file.txt"
)|;
assert(File.new("file.txt"
).asPrintable == "File(\"file.txt\")"
);
- asString
The name of the opened file.
System.system("(echo 1; echo 2) >file.txt"
)|;
assert(File.new("file.txt"
).asString == "file.txt"
);
- content
The content of the file as a Binary (??sec:std-Binary) object.
System.system("(echo 1; echo 2) >file.txt"
)|;
assert(File.new("file.txt"
).content.data == "1\n2\n"
);
- create(name)
If the file name exists, return a File to it, otherwise create an empty one, and return a File to it.
See OutputStream (??sec:std-OutputStream) for methods to add content to the file.
System.system("(echo 1; echo 2) >file.txt"
)|;
assert
{
File.create("file.txt"
).asPrintable == "File(\"file.txt\")"
;
File.new("file.txt"
).content.data == "1\n2\n"
;
File.create("new.txt"
).content.empty;
};
- remove
Remove the current file.
System.system("(echo 1; echo 2) >file.txt"
)|;
File.new("file.txt"
).remove;
assert(!Path.new("file.txt"
).exists);
- rename(name)
Rename the file to name. If the target exists, it is replaced by the opened file.
System.system("(echo 1; echo 2) >foo.txt"
)|;
File.new("foo.txt"
).rename("bar.txt"
);
assert
{
!Path.new("foo.txt"
).exists;
File.new("bar.txt"
).content.data == "1\n2\n"
;
};
20.18 Finalizable
Objects that derive from this object will execute their finalize routine right before being
destroyed (reclaimed) by the system. It is comparable to a destructor.
The following object is set up to die verbosely.
var obj =
do (Finalizable.new)
{
function finalize ()
{
echo ("Ouch"
);
}
}|;
It is reclaimed by the system when it is no longer referenced by any other object.
var alias = obj|;
obj = nil|;
Here, the object is still alive, since alias references it. Once it no longer does, the object
dies.
alias = nil|;
[00000004] *** Ouch
The constructor takes no argument.
Finalizable.new;
[00000527] Finalizable_0x135360
Because of specific constraints of Finalizable, you cannot change the prototype of an object to
make it “finalizable”: it must be an instance of Finalizable from its inception.
There, instead of these two invalid constructs,
|
class o1 : Finalizable.new { function finalize() { echo("Ouch" ); } }|;
[00000008:error] !!! <empty>: cannot inherit from a Finalizable without being a Finalizable too
class o2 { protos = [Finalizable]; function finalize() { echo("Ouch" ); } }|;
[00000010:error] !!! updateSlot: cannot inherit from a Finalizable without being a Finalizable too
|
write:
var o3 =
do (Finalizable.new)
{
function finalize()
{
echo("Ouch"
);
}
}|;
If you need multiple prototypes, do as follows.
class Global.Foo
{
function init()
{
echo("1"
);
};
}|;
class Global.FinalizableFoo
{
addProto(Foo.new);
function ’new’()
{
var r = clone |
r.init |
Finalizable.new.addProto(r);
};
function init()
{
echo("2"
);
};
function finalize()
{
echo("3"
);
};
}|;
var i = FinalizableFoo.new|;
[00000117] *** 1
[00000117] *** 2
i = nil;
[00000117] *** 3
- finalize
a simple function that takes no argument that will be evaluated when the object is reclaimed. Its
return value is ignored.
Finalizable.new.setSlot("finalize"
, function() { echo("Ouch"
) })|;
[00033240] *** Ouch
20.19 Float
A Float is a floating point number. It is also used, in the current version of urbiscript, to represent
integers.
The most common way to create fresh floats is using the literal syntax. Numbers are composed of three
parts:
-
integral
- (mandatory) a non empty sequence of (decimal) digits;
-
fractional
- (optional) a period, and a non empty sequence of (decimal) digits;
-
exponent
- (optional) either ‘e’ or ‘E’, an optional sign (‘+’ or ‘-’), then a non-empty sequence
of digits.
In other words, float literals match the [0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)? regular
expression. For instance:
0 == 0000.0000;
+1 == 1;
0.123456 == 123456 / 1000000;
1e3 == 1000;
1e-3 == 0.001;
1.234e3 == 1234;
There are also some special numbers, nan, inf (see below).
Math.log(0) == -inf;
Math.exp(-inf) == 0;
(inf/inf).asString == "nan"
;
A null float can also be obtained with Float’s new method.
20.20 Float.limits
This singleton handles various limits related to the Float objects.
- digits
Number of digits (in radix base) in the mantissa.
- digits10
Number of digits (in decimal base) that can be represented without change.
- epsilon
Machine epsilon (the difference between 1 and the least value greater than 1 that is
representable).
1 != 1 + Float.limits.epsilon;
1 == 1 + Float.limits.epsilon / 2;
- max
Maximum finite value.
Float.limits.max != Float.inf;
Float.limits.max * 2 == Float.inf;
- maxExponent
Maximum integer value for the exponent that generates a normalized floating-point number.
Float.inf != Float.limits.radix ** (Float.limits.maxExponent - 1);
Float.inf == Float.limits.radix ** Float.limits.maxExponent;
- maxExponent10
Maximum integer value such that 10 raised to that power generates a normalized finite
floating-point number.
Float.inf != 10 ** Float.limits.maxExponent10;
Float.inf == 10 ** (Float.limits.maxExponent10 + 1);
- min
Minimum positive normalized value.
- minExponent
Minimum negative integer value for the exponent that generates a normalized floating-point
number.
0 != Float.limits.radix ** Float.limits.minExponent;
- minExponent10
Minimum negative integer value such that 10 raised to that power generates a normalized
floating-point number.
0 != 10 ** Float.limits.minExponent10;
- radix
Base of the exponent of the representation.
20.21 FormatInfo
A format info is used when formatting a la printf. It store the formatting pattern itself and all
the format information it can extract from the pattern.
The constructor expects a string as argument, whose syntax is similar to printf’s. It is detailed
below.
var f = FormatInfo.new("%+2.3d"
);
[00000001] %+2.3d
A formatting pattern must one of the following (brackets denote optional arguments):
- %options spec
- %|options[spec]|
options is a sequence of 0 or several of the following characters:
|
|
| ‘-’ | Left alignment. |
| ‘=’ | Centered alignment. |
| ‘+’ | Show sign even for positive number. |
| ‘ ’ | If the string does not begin with ‘+’ or ‘-’, insert a space before the converted string. |
| ‘0’ | Pad with 0’s (inserted after sign or base indicator). |
| ‘#’ | Show numerical base, and decimal point. |
|
|
| |
spec is the conversion character and must be one of the following:
|
|
| ‘s’ | Default character, prints normally |
| ‘d’ | Case modifier: lowercase |
| ‘D’ | Case modifier: uppercase |
| ‘x’ | Prints in hexadecimal lowercase |
| ‘X’ | Prints in hexadecimal uppercase |
| ‘o’ | Prints in octal |
| ‘e’ | Prints floats in scientific format |
| ‘E’ | Prints floats in scientific format uppercase |
| ‘f’ | Prints floats in fixed format |
|
|
| |
- alignment
Requested alignment: -1 for left, 0 for centered, 1 for right (default).
FormatInfo.new("%s"
).alignment == 1;
FormatInfo.new("%=s"
).alignment == 0;
FormatInfo.new("%-s"
).alignment == -1;
- alt
Whether the “alternative” display is requested (‘#’).
FormatInfo.new("%s"
).alt == false;
FormatInfo.new("%#s"
).alt == true;
- group
Separator to use for thousands. Corresponds to the ‘’’ option.
FormatInfo.new("%s"
).group == ""
;
FormatInfo.new("%’s"
).group == " "
;
- pad
The padding character to use for alignment requests. Defaults to space.
FormatInfo.new("%s"
).pad == " "
;
FormatInfo.new("%0s"
).pad == "0"
;
- pattern
The pattern given to the constructor.
FormatInfo.new("%#’12.8s"
).pattern == "%#’12.8s"
;
- precision
When formatting a Float (??sec:std-Float), the maximum number of digits after decimal
point when in fixed or scientific mode, and in total when in default mode. When
formatting other objects with spec-char ‘s’, the conversion string is truncated to
the precision first chars. The eventual padding to width is done after truncation.
FormatInfo.new("%s"
).precision == 6;
FormatInfo.new("%23.3s"
).precision == 3;
- prefix
The string to display before positive numbers. Defaults to empty.
FormatInfo.new("%s"
).prefix == ""
;
FormatInfo.new("% s"
).prefix == " "
;
FormatInfo.new("%+s"
).prefix == "+"
;
- spec
The specification character, regardless of the case conversion requests.
FormatInfo.new("%s"
).spec == "s"
;
FormatInfo.new("%23.3s"
).spec == "s"
;
FormatInfo.new("%’X"
).spec == "x"
;
- uppercase
Case conversion: -1 for lower case, 0 for no conversion (default), 1 for conversion to uppercase.
The value depends on the case of specification character, except for ‘%s’ which corresponds to 0.
FormatInfo.new("%s"
).uppercase == 0;
FormatInfo.new("%d"
).uppercase == -1;
FormatInfo.new("%D"
).uppercase == 1;
FormatInfo.new("%x"
).uppercase == -1;
FormatInfo.new("%X"
).uppercase == 1;
- width
Width requested for alignment.
FormatInfo.new("%s"
).width == 0;
FormatInfo.new("%10s"
).width == 10;
20.22 Formatter
A formatter stores format information of a format string like used in printf in the C library or in
boost::format.
Formatters are created with the format string. It cuts the string to separate regular parts of string and
formatting patterns, and stores them.
Formatter.new("Name:%s, Surname:%s;"
);
[00000001] Formatter ["Name:", %s, ", Surname:", %s, ";"]
Actually, formatting patterns are translated into FormatInfo (??sec:std-FormatInfo).
- asList
Return the content of the formatter as a list of strings and FormatInfo (??sec:std-FormatInfo).
Formatter.new("Name:%s, Surname:%s;"
).asList.asString
== "[\"Name:\", %s, \", Surname:\", %s, \";\"]"
;
- ’%’(args)
Use this as format string and args as the list of arguments, and return the result (a String
(??sec:std-String)). The arity of the Formatter (i.e., the number of expected arguments) and the
size of args must match exactly.
This operator concatenates regular strings and the strings that are result of asString
called on members of args with the appropriate FormatInfo (??sec:std-FormatInfo).
Formatter.new("Name:%s, Surname:%s;"
) % ["Foo"
, "Bar"
]
== "Name:Foo, Surname:Bar;"
;
If args is not a List (??sec:std-List), then the call is equivalent to calling —’
Formatter.new("%06.3f"
) % Math.pi
== "03.142"
;
Note that String.’%’ provides a nicer interface to this operator:
"%06.3f"
% Math.pi == "03.142"
;
It is nevertheless interesting to use the Formatter for performance reasons if the format is reused
many times.
{
var people =
[["Foo"
, "Bar"
],
["One"
, "Two"
],
["Un"
, "Deux"
],];
var f = Formatter.new("Name:%7s, Surname:%7s;"
);
for (var p: people)
echo (f % p);
};
[00031939] *** Name: Foo, Surname: Bar;
[00031940] *** Name: One, Surname: Two;
[00031941] *** Name: Un, Surname: Deux;
20.23 Global
Global is designed for the purpose of being global namespace. Since Global is a prototype of Object
and all objects are an Object, all slots of Global are accessible from anywhere.
20.24 Group
A transparent means to send messages to several objects as if they were one.
The following session demonstrates the features of the Group objects. It first creates the Sample family
of object, makes a group of such object, and uses that group.
class Sample
{
var value = 0;
function init(v) { value = v; };
function asString() { "<"
+ value.asString + ">"
; };
function timesTen() { new(value * 10); };
function plusTwo() { new(value + 2); };
};
[00000000] <0>
var group = Group.new(Sample.new(1), Sample.new(2));
[00000000] Group [<1>, <2>]
group << Sample.new(3);
[00000000] Group [<1>, <2>, <3>]
group.timesTen.plusTwo;
[00000000] Group [<12>, <22>, <32>]
group.value;
[00000000] Group [1, 2, 3]
group.value = 10;
[00000000] Group [10, 10, 10]
var sum = 0|
for& (var v : group)
sum += v.value;
sum;
[00000000] 30
Groups are created like any other object. The constructor can take members to add to the
group.
Group.new;
[00000000] Group []
Group.new(1, "two"
);
[00000000] Group [1, "two"]
- add(member, ...)
Add members to this group, and return this.
- asString
Report the members.
- each(action)
Apply action to all the members, in sequence, then return the Group of the results, in
the same order. Allows to iterate over a Group via for.
- each&(action)
Apply action to all the members, concurrently, then return the Group of the results. The
order is not necessarily the same. Allows to iterate over a Group via for&.
- fallback
This function is called when a method call on this failed. It bounces the call to the
members of the group, collects the results returned as a group. This allows to chain grouped
operation in a row. If the dispatched calls return void, returns a single void, not a “group
of void”.
- getProperty(slot, prop)
Bounced to the members so that this.slot->prop actually collects the values of the
property prop of the slots slot of the group members.
- hasProperty(name)
Bounced to the members.
- remove(member, ...)
Remove members from this group, and return this.
- setProperty(slot, prop, value)
Bounced to the members so that this.slot->prop = value actually updates the value
of the property prop in the slots slot of the group members.
- updateSlot(name, value)
Bounced to the members so that this.name = value actually updates the value of the
slot name in the group members.
- ’<<’(member)
Syntactic sugar for add.
20.25 InputStream
InputStreams are used to read (possibly binary) files by hand. File (??sec:std-File) provides means
to swallow a whole file either as a single large string, or a list of lines. InputStream provides a more
fine-grained interface to read files.
Windows Issues
Beware that because of limitations in the current implementation, one cannot safely
read from two different files at the same time under Windows.
An InputStream is a reading-interface to a file, so its constructor requires a File (??sec:std-File).
System.system("(echo 1; echo 2) >file.txt"
)|;
InputStream.new(File.new("file.txt"
));
[00000001] InputStream_0x827000
- get
Get the next available byte as a Float (??sec:std-Float), or void if the end of file was reached.
System.system("(echo 1; echo 2) >file.txt"
)|;
assert(
{
var res = [];
var i = InputStream.new(File.new("file.txt"
));
var x;
while (!(x = i.get.acceptVoid).isVoid)
res << x;
res;
}
==
[49, 10, 50, 10]);
- getChar
Get the next available byte as a String (??sec:std-String), or void if the end of file was reached.
System.system("(echo 1; echo 2) >file.txt"
)|;
assert(
{
var res = [];
var i = InputStream.new(File.new("file.txt"
));
var x;
while (!(x = i.getChar.acceptVoid).isVoid)
res << x;
res;
}
==
["1"
, "\n"
, "2"
, "\n"
]);
- getLine
Get the next available line as a String (??sec:std-String), or void if the end of file was reached.
System.system("(echo 1; echo 2) >file.txt"
)|;
assert(
{
var res = [];
var i = InputStream.new(File.new("file.txt"
));
var x;
while (!(x = i.getLine.acceptVoid).isVoid)
res << x;
res;
}
==
["1"
, "2"
]);
20.26 IoService
A IoService is used to manage the various operations of a set of Socket (??sec:std-Socket).
All Socket (??sec:std-Socket) and Server (??sec:std-Server) are by default using the default
IoService (??sec:std-IoService) which is polled regularly by the system.
Using a different IoService (??sec:std-IoService) is required if you need to perform synchronous read
operations.
The Socket (??sec:std-Socket) must be created by the IoService (??sec:std-IoService) that will
handle it using its makeSocket function.
var io = IoService.new|;
var s = io.makeSocket|;
You can then use this socket like any other.
var serverPort = 0|
do(Server.new)
{
listen("127.0.0.1"
, "0"
);
lobby.serverPort = port;
at(connection?(var s))
{
s.write("hello"
);
}
}|;
s.connect("0.0.0.0"
, serverPort);
at(s.received?(var data))
echo("received something"
);
s.write("1;"
);
... except that nothing will be read from the socket unless you call one of the poll functions of
io.
sleep(200ms);
s.isConnected();
[00000001] true
io.poll();
[00000002] *** received something
sleep(200ms);
A IoService is constructed with no argument.
20.27 Job
Jobs are independent threads of executions. Jobs can run concurrently. They can also be managed
using Tags (??sec:std-Tag).
A Job is typically constructed via Control.detach, Control.disown, or System.spawn.
detach(sleep(10));
[00202654] Job<shell_4>
disown(sleep(10));
[00204195] Job<shell_5>
spawn (function () { sleep(10) }, false);
[00274160] Job<shell_6>
- asJob
Return this.
- asString
The string Job<name> where name is the name of the job.
- backtrace
The current backtrace of the job as a list of StackFrames (??sec:std-StackFrame).
var s = detach(sleep(10))|;
sleep(1);
assert
{
s.backtrace[0].asString == "file.u:1.16-24: sleep"
;
s.backtrace[1].asString == "file.u:1.9-25: detach"
;
};
- clone
Cloning a job is impossible since Job is considered as being an atom.
- dumpState
Pretty-print the state of the job.
var t = detach(sleep(10))|;
sleep(1);
t.dumpState;
[00004295] *** Job: shell_10
[00004295] *** State: sleeping
[00004295] *** Tags:
[00004295] *** Tag<Lobby_1>
[00004297] *** Backtrace:
[00004297] *** file.u:1.16-24: sleep
[00004297] *** file.u:1.9-25: detach
- name
The name of the job.
detach(sleep(10)).name;
[00004297] "shell_5"
- setSideEffectFree(value)
If value is true, mark the current job as side-effect free. It indicates whether the current state
may influence other parts of the system. This is used by the scheduler to choose whether other
jobs need scheduling or not. The default value is false.
- status
The current status of the job (starting, running, …), and its properties (frozen, …).
- tags
The list of Tags (??sec:std-Tag) that manage this job.
- terminate
Kill this job.
var r = detach({ sleep(1s); echo("done"
) })|;
assert (r in jobs);
r.terminate;
assert (r not in jobs);
sleep(2s);
- timeShift
Get the total amount of time during which we were frozen.
tag: r = detach({ sleep(3); echo("done"
) })|;
tag.freeze();
sleep(2);
tag.unfreeze();
Math.round(r.timeShift);
[00000001] 2
- waitForChanges
Resume the scheduler, putting the current Job in a waiting status. The scheduler may reschedule
the job immediately.
- waitForTermination
Wait for the job to terminate before resuming execution of the current one. If the job has already
terminated, return immediately.
20.28 Kernel1
This object plays the role of a name-space in which obsolete functions from urbiscript
1.0 are provided for backward compatibility. Do not use these functions, scheduled for
removal.
Since it is a Singleton (??sec:std-Singleton), you are not expected to build other instances.
- commands
Ignored for backward compatibility.
- connections
Ignored for backward compatibility.
- copy(binary)
Obsolete syntax for binary.copy, see Binary (??sec:std-Binary).
var a = BIN 10;0123456789
[00000001] BIN 10
0123456789
var b = Kernel1.copy(a);
[00000003:warning] *** ‘copy(binary)’ is deprecated, use ‘binary.copy’
[00000004] BIN 10
0123456789
echo (b);
[00000005] *** BIN 10
0123456789
- devices
Ignored for backward compatibility.
- events
Ignored for backward compatibility.
- functions
Ignored for backward compatibility.
- isvoid(obj)
Obsolete syntax for obj.isVoid, see Object (??sec:std-Object).
- noop
Do nothing. Use {} instead.
- ping
Return time verbosely, see System (??sec:std-System).
Kernel1.ping;
[00000421] *** pong time=0.12s
- reset
Ignored for backward compatibility.
- runningcommands
Ignored for backward compatibility.
- seq(number)
Obsolete syntax for number.seq, see Float (??sec:std-Float).
- size(list)
Obsolete syntax for list.size, see List (??sec:std-List).
Kernel1.size([1, 2, 3]) == [1, 2, 3].size;
[00000002:warning] *** ‘size(list)’ is deprecated, use ‘list.size’
- strict
Ignored for backward compatibility.
- strlen(string)
Obsolete syntax for string.length, see String (??sec:std-String).
Kernel1.strlen("123"
) == "123"
.length;
[00000002:warning] *** ‘strlen(string)’ is deprecated, use ‘string.length’
- taglist
Ignored for backward compatibility.
- undefall
Ignored for backward compatibility.
- unstrict
Ignored for backward compatibility.
- uservars
Ignored for backward compatibility.
- vars
Ignored for backward compatibility.
20.29 Lazy
Lazies are objects that hold a lazy value, that is, a not yet evaluated value. They provide facilities
to evaluate their content only once (memoization) or several times. Lazy are essentially used in call
messages, to represent lazy arguments, as described in Section 20.4.
One usage of lazy values is to avoid evaluating an expression unless it’s actually needed, because it’s
expensive or has undesired side effects. The listing below presents a situation where an
expensive-to-compute value (heavy_computation) might be needed zero, one or two times. The
objective is to save time by:
- Not evaluating it if it’s not needed.
- Evaluating it only once if it’s needed once or twice.
We thus make the wanted expression lazy, and use the value method to fetch its value when
needed.
function heavy_computation()
{
echo("Heavy computation"
);
return 1 + 1;
}|;
var v = Lazy.new(closure () { heavy_computation() });
[00000000] heavy_computation()
;
;
v.value;
[00000000] *** Heavy computation
[00000000] 2
v.value;
[00000000] 2
Evaluating a lazy several times only makes sense with lazy arguments and call messages. See example
with call messages in Section 20.4.1.1.
Lazy (??sec:std-Lazy) is meant for functions without argument. If you need caching for
functions that depend on arguments, it is straightforward to implement using a Dictionary
(??sec:std-Dictionary). In the future urbiscript might support dictionaries whose indices are not only
strings, but in the meanwhile, convert the arguments into strings, as the following sample object
demonstrates.
class UnaryLazy
{
function init(f)
{
results = [ => ];
func = f;
};
function value(p)
{
var sp = p.asString;
if (results.has(sp))
return results[sp];
var res = func(p);
results[sp] = res |
res
};
var results;
var func;
} |
var inc = function(x) { echo("incing "
+ x) | x+1 } |
var p = UnaryLazy.new(getSlot("inc"
));
[00062847] UnaryLazy_0x78b750
p.value(1);
[00066758] *** incing 1
[00066759] 2
p.value(1);
[00069058] 2
p.value(2);
[00071558] *** incing 2
[00071559] 3
p.value(2);
[00072762] 3
p.value(1);
[00074562] 2
Lazies are seldom instantiated manually. They are mainly created automatically when a lazy function
call is made (see Section 19.3.4). One can however create a lazy value with the standard new
method of Lazy, giving it an argument-less function which evaluates to the value made
lazy.
Lazy.new(closure () { 0 });
[00000000] 0
- Whether this and that are the same source code and value (an unevaluated Lazy is never equal
to an evaluated one).
Lazy.new(closure () { 1 + 1 }) == Lazy.new(closure () { 1 + 1 });
Lazy.new(closure () { 1 + 2 }) != Lazy.new(closure () { 2 + 1 });
{
var l1 = Lazy.new(closure () { 1 + 1 });
var l2 = Lazy.new(closure () { 1 + 1 });
assert (l1 == l2);
l1.eval;
assert (l1 != l2);
l2.eval;
assert (l1 == l2);
};
- asString
The conversion to String (??sec:std-String) of the body of a non-evaluated argument.
Lazy.new(closure () { echo(1); 1 }).asString == "echo(1);\n1"
;
- eval
Force the evaluation of the held lazy value. Two calls to eval will systematically evaluate the
expression twice, which can be useful to duplicate its side effects.
- value
Return the held value, potentially evaluating it before. value performs memoization, that is, only
the first call will actually evaluate the expression, subsequent calls will return the cached value.
Unless you want to explicitly trigger side effects from the expression by evaluating it several time,
this should be preferred over eval to avoid evaluating the expression several times
uselessly.
20.30 List
Lists implement potentially-empty ordered (heterogeneous) collections of elements.
List can be created with their literal syntax: a possibly empty sequence of expressions in square
brackets, separated by commas. Non-empty list may actually terminate with a comma, rather than
separate; in other words, an optional trailing comma is accepted.
[];
[00000000] []
[1, "2"
, [3,],];
[00000000] [1, "2", [3]]
- argMax(fun = function(a, b)
a ¡ b )The index of the (leftmost) “largest” member based on the comparison function fun.
[1].argMax == 0;
[1, 2].argMax == 1;
[1, 2, 2].argMax == 1;
[2, 1].argMax == 0;
[2, -1, 3, -4].argMax == 2;
[2, -1, 3, -4].argMax (function (a, b) { a.abs < b.abs }) == 3;
The list cannot be empty.
[].argMax;
[00000001:error] !!! list cannot be empty
- argMin(fun = function(a, b)
a ¡ b )The index of the (leftmost) “smallest” member based on the comparison function fun.
[1].argMin == 0;
[1, 2].argMin == 0;
[1, 2, 1].argMin == 0;
[2, 1].argMin == 1;
[2, -1, 3, -4].argMin == 3;
[2, -1, 3, -4].argMin (function (a, b) { a.abs < b.abs }) == 1;
The list cannot be empty.
[].argMin;
[00000001:error] !!! list cannot be empty
- asBool
Whether not empty.
[].asBool == false;
[1].asBool == true;
- asList
Return the target.
{
var l = [0, 1, 2];
assert (l.asList === l);
};
- asString
A string describing the list. Uses asPrintable on its members, so that, for instance, strings are
displayed with quotes.
[0, [1], "2"
].asString == "[0, [1], \"2\"]"
;
- back
The last element of the target. An error if the target is empty.
assert([0, 1, 2].back == 2);
[].back;
[00000000:error] !!! back: cannot be applied onto empty list
- clear
Empty the target.
var x = [0, 1, 2];
[00000000] [0, 1, 2]
assert(x.clear == []);
- each(fun)
Apply the given functional value fun on all members, sequentially.
[0, 1, 2].each(function (v) {echo (v * v); echo (v * v)});
[00000000] *** 0
[00000000] *** 0
[00000000] *** 1
[00000000] *** 1
[00000000] *** 4
[00000000] *** 4
- eachi(fun)
Apply the given functional value fun on all members sequentially, additionally passing the
current element index.
["a"
, "b"
, "c"
].eachi(function (v, i) {echo ("%s: %s"
% [i, v])});
[00000000] *** 0: a
[00000000] *** 1: b
[00000000] *** 2: c
- ’each&’(fun)
Apply the given functional value on all members simultaneously.
[0, 1, 2].’each&’(function (v) {echo (v * v); echo (v * v)});
[00000000] *** 0
[00000000] *** 1
[00000000] *** 4
[00000000] *** 0
[00000000] *** 1
[00000000] *** 4
- empty
Whether the target is empty.
- filter(fun)
The list of all the members of the target that verify the predicate fun.
do ([0, 1, 2, 3, 4, 5])
{
assert
{
filter(function (v) {v % 2 == 1}) == [1, 3, 5];
filter(function (v) { true }) == this;
filter(function (v) { false }) == [];
};
}|;
- foldl(action, value)
Fold, also known as reduce or accumulate, computes a result from a list. Starting from value as
the initial result, apply repeatedly the binary action to the current result and the next member
of the list, from left to right. For instance, if action were the binary addition and value
were 0, then folding a list would compute the sum of the list, including for empty
lists.
[].foldl(function (a, b) { a + b }, 0) == 0;
[1, 2, 3].foldl(function (a, b) { a + b }, 0) == 6;
[1, 2, 3].foldl(function (a, b) { a - b }, 0) == -6;
- front
Return the first element of the target. An error if the target is empty.
assert([0, 1, 2].front == 0);
[].front;
[00000000:error] !!! front: cannot be applied onto empty list
- has(that)
Whether one of the members of the target equals the argument.
[0, 1, 2].has(1);
! [0, 1, 2].has(5);
The infix operators in and not in use has (see Section 19.1.8.6).
1 in [0, 1];
2 not in [0, 1];
!(2 in [0, 1]);
!(1 not in [0, 1]);
- hasSame(that)
Whether one of the members of the target is physically equal to that.
var y = 1|;
assert
{
[0, y, 2].hasSame(y);
![0, y, 2].hasSame(1);
};
- head
Synonym for front.
assert([0, 1, 2].head == 0);
[].head;
[00000000:error] !!! head: cannot be applied onto empty list
- insertBack(that)
Insert the given element at the end of the target.
do ([0, 1])
{
assert
{
insertBack(2) == [0, 1, 2];
this == [0, 1, 2];
};
}|;
- insert(where, what)
Insert what before the value at index where, return this.
do ([0, 1])
{
assert
{
insert(0, 10) == [10, 0, 1];
this == [10, 0, 1];
insert(2, 20) == [10, 0, 20, 1];
this == [10, 0, 20, 1];
};
insert(4, 30);
}|;
[00044239:error] !!! insert: invalid index: 4
The index must be valid, to insert past the end, use insertBack.
[].insert(0, "foo"
);
[00044239:error] !!! insert: invalid index: 0
- insertFront(that)
Insert the given element at the beginning of the target.
do ([1, 2])
{
assert
{
insertFront(0) == [0, 1, 2];
this == [0, 1, 2];
};
}|;
- join(sep = "", prefix = "", suffix = "")
Bounce to String.join.
[""
, "ob"
, ""
].join == "ob"
;
[""
, "ob"
, ""
].join("a"
) == "aoba"
;
[""
, "ob"
, ""
].join("a"
, "B"
, "b"
) == "Baobab"
;
- keys
The list of valid indexes. This allows uniform iteration over a Dictionary (??sec:std-Dictionary)
or a List (??sec:std-List).
{
var l = ["a"
, "b"
, "c"
];
assert
{
l.keys == [0, 1, 2];
{
var res = [];
for (var k: l.keys)
res << l[k];
res
}
== l;
};
};
- map(fun)
Apply the given functional value on every member, and return the list of results.
[0, 1, 2, 3].map(function (v) { v % 2 == 0})
== [true, false, true, false];
- max(fun = function(a, b)
a ¡ b ) Return the “largest” member based on the comparison function fun.
[1].max == 1;
[1, 2].max == 2;
[2, 1].max == 2;
[2, -1, 3, -4].max == 3;
[2, -1, 3, -4].max (function (a, b) { a.abs < b.abs }) == -4;
The list cannot be empty.
[].max;
[00000001:error] !!! list cannot be empty
- min(fun = function(a, b)
a ¡ b ) Return the “smallest” member based on the comparison function fun.
[1].min == 1;
[1, 2].min == 1;
[2, 1].min == 1;
[2, -1, 3, -4].min == -4;
[2, -1, 3, -4].min (function (a, b) { a.abs < b.abs }) == -1;
The list cannot be empty.
[].min;
[00000001:error] !!! list cannot be empty
- range(begin, end = nil)
Return a sub-range of the list, from the first index included to the second index
excluded. An error if out of bounds. Negative indices are valid, and number from the
end.
If end is nil, calling range(n) is equivalent to calling range(0, n).
do ([0, 1, 2, 3])
{
assert
{
range(0, 0) == [];
range(0, 1) == [0];
range(1) == [0];
range(1, 3) == [1, 2];
range(-3, -2) == [1];
range(-3, -1) == [1, 2];
range(-3, 0) == [1, 2, 3];
range(-3, 1) == [1, 2, 3, 0];
range(-4, 4) == [0, 1, 2, 3, 0, 1, 2, 3];
};
}|;
[].range(1, 3);
[00428697:error] !!! range: invalid index: 1
- remove(val)
Remove all elements from the target that are equal to val, return this.
var c = [0, 1, 0, 2, 0, 3]|;
assert
{
c.remove(0) === c; c == [1, 2, 3];
c.remove(42) === c; c == [1, 2, 3];
};
- removeBack
Remove and return the last element of the target. An error if the target is empty.
var t = [0, 1, 2];
[00000000] [0, 1, 2]
assert(t.removeBack == 2);
assert(t == [0, 1]);
[].removeBack;
[00000000:error] !!! removeBack: cannot be applied onto empty list
- removeById(that)
Remove all elements from the target that physically equals that.
var d = 1|;
var e = [0, 1, d, 1, 2]|;
assert
{
e.removeById(d) == [0, 1, 1, 2];
e == [0, 1, 1, 2];
};
- removeFront
Remove and return the first element from the target. An error if the target is empty.
var g = [0, 1, 2]|;
assert
{
g.removeFront == 0;
g == [1, 2];
};
[].removeFront;
[00000000:error] !!! removeFront: cannot be applied onto empty list
- reverse
Return the target with the order of elements inverted.
[0, 1, 2].reverse == [2, 1, 0];
- size
Return the number of elements in the target.
[0, 1, 2].size == 3;
[].size == 0;
- sort
Return the target, sorted with respect to the < criteria.
[1, 0, 3, 2].sort == [0, 1, 2, 3];
- subset(that)
Whether the members of this are members of that.
[].subset([]);
[].subset([1, 2, 3]);
[3, 2, 1].subset([1, 2, 3]);
[1, 3].subset([1, 2, 3]);
[1, 1].subset([1, 2, 3]);
![3].subset([]);
![3, 2].subset([1, 2]);
![1, 2, 3].subset([1, 2]);
- tail
Return the target, minus the first element. An error if the target is empty.
assert([0, 1, 2].tail == [1, 2]);
[].tail;
[00000000:error] !!! tail: cannot be applied onto empty list
- zip(fun, other)
Zip this list and the other list with the fun function, and return the list of results.
[1, 2, 3].zip(closure (x, y) { (x, y) }, [4, 5, 6])
== [(1, 4), (2, 5), (3, 6)];
[1, 2, 3].zip(closure (x, y) { x + y }, [4, 5, 6])
== [5, 7, 9];
- ’==’(that)
Check whether all elements in the target and that, are equal two by two.
[0, 1, 2] == [0, 1, 2];
!([0, 1, 2] == [0, 0, 2]);
- ’[]’(n)
Return the nth member of the target (indexing is zero-based). If n is negative, start from the end.
An error if out of bounds.
assert(["0"
, "1"
, "2"
][0] == "0"
);
assert(["0"
, "1"
, "2"
][2] == "2"
);
["0"
, "1"
, "2"
][3];
[00007061:error] !!! []: invalid index: 3
assert(["0"
, "1"
, "2"
][-1] == "2"
);
assert(["0"
, "1"
, "2"
][-3] == "0"
);
["0"
, "1"
, "2"
][-4];
[00007061:error] !!! []: invalid index: -4
- ’[]=’(index, value)
Assign value to the element of the target at the given index.
var f = [0, 1, 2];
[00000000] [0, 1, 2]
f[1] = 42;
[00000000] 42
assert(f == [0, 42, 2]);
- ’*’(n)
Return the target, concatenated n times to itself.
[0, 1] * 0 == [];
[0, 1] * 3 == [0, 1, 0, 1, 0, 1];
n must be a non-negative integer.
[0, 1] * -2;
[00000063:error] !!! *: expected non-negative integer, got -2
Note that since it is the very same list which is repeatedly concatenated (the content is not
cloned), side-effects on one item will reflect on “all the items”.
var l = [[]] * 3;
[00000000] [[], [], []]
l[0] << 1;
[00000000] [1]
l;
[00000000] [[1], [1], [1]]
- ’+’(other)
Return the concatenation of the target and the other list.
[0, 1] + [2, 3] == [0, 1, 2, 3];
[] + [2, 3] == [2, 3];
[0, 1] + [] == [0, 1];
[] + [] == [];
- ’-’(other)
Return the target without the elements that are equal to any element in the other
list.
[0, 1, 0, 2, 3] - [1, 2] == [0, 0, 3];
[0, 1, 0, 1, 0] - [1, 2] == [0, 0, 0];
- ’<<’(that)
A synonym for insertBack.
- ’<’(other)
Return whether this is less than the other list. This is the lexicographic comparison:
this is “less that” if one of its member is “less than” the corresponding member of
other:
[0, 0, 0] < [0, 0, 1];
[0, 1, 2] < [0, 2, 1];
!([0, 1, 2] < [0, 1, 2]);
!([0, 1, 2] < [0, 0, 2]);
or other is a prefix (strict) of this:
[] < [0]; !( [0] < []);
[0, 1] < [0, 1, 2]; !([0, 1, 2] < [0, 1]);
!([0, 1, 2] < [0, 1, 2]);
Since List derives from Orderable (??sec:std-Orderable), the other order-based operators are
defined.
[] <= [];
[] <= [0, 1, 2];
[0, 1, 2] <= [0, 1, 2];
[] >= [];
[0, 1, 2] >= [];
[0, 1, 2] >= [0, 1, 2];
[0, 1, 2] >= [0, 0, 2];
!([] > []);
[0, 1, 2] > [];
!([0, 1, 2] > [0, 1, 2]);
[0, 1, 2] > [0, 0, 2];
20.31 Loadable
Loadable objects can be switched on and off — typically physical devices.
The intended use is rather as follows:
class Motor: Loadable
{
var val = 0;
function go(var d)
{
if (load)
val += d
else
echo("cannot advance, the motor is off"
)|;
};
};
[00000002] Motor
var m = Motor.new;
[00000003] Motor_0xADDR
m.load;
[00000004] false
m.go(1);
[00000006] *** cannot advance, the motor is off
m.on;
[00000007] Motor_0xADDR
m.go(123);
m.val;
[00000009] 123
Loadable can be constructed, but it hardly makes sense. This object should serve as a
prototype.
- load
The current status.
- off(val)
Set load to false and return this.
do (Loadable.new)
{
assert
{
!load;
off === this;
!load;
on === this;
load;
off === this;
!load;
};
};
- on(val)
Set load to true and return this.
do (Loadable.new)
{
assert
{
!load;
on === this;
load;
on === this;
load;
};
};
- toggle
Set load from true to false, and vice-versa. Return val.
do (Loadable.new)
{
assert
{
!load;
toggle === this;
load;
toggle === this;
!load;
};
};
20.32 Lobby
A lobby is the local environment for each (remote or local) connection to an Urbi server.
A lobby is implicitly created at each connection. At the top level, this is a Lobby.
this.protos;
[00000001] [Lobby]
this.protos[0].protos;
[00000003] [Channel_0xADDR]
Lobbies cannot be cloned, they must be created using create.
Lobby.new;
[00000177:error] !!! new: ‘Lobby’ objects cannot be cloned
Lobby.create;
[00000174] Lobby_0x126450
Since every lobby is-a Channel (??sec:std-Channel), one can use the methods of Channel.
lobby << 123;
[00478679] 123
lobby << "foo"
;
[00478679] "foo"
20.33 Location
This class aggregates two Positions and provides a way to print them as done in error
messages.
Without argument, a newly constructed Location has its Positions initialized to the first line and the
first column.
Location.new;
[00000001] 1.1
With a Position argument p, the Location will clone the Position into the begin and end
Positions.
Location.new(Position.new("file.u"
,14,25));
[00000001] file.u:14.25
With two Positions arguments begin and end, the Location will clone both Positions into its own
fields.
Location.new(Position.new("file.u"
,14,25), Position.new("file.u"
,14,35));
[00000001] file.u:14.25-34
20.34 Math
This object is actually meant to play the role of a name-space in which the mathematical functions
are defined with a more conventional notation. Indeed, in an object-oriented language, writing pi.cos
makes perfect sense, yet cos(pi) is more usual.
Since it is a Singleton (??sec:std-Singleton), you are not expected to build other instances.
- abs(float)
Bounce to float.abs.
Math.abs(1) == 1;
Math.abs(-1) == 1;
Math.abs(0) == 0;
Math.abs(3.5) == 3.5;
- acos(float)
Bounce to float.acos.
- asin(float)
Bounce to float.asin.
- atan(float)
Bounce to float.atan.
- atan2(x, y)
Bounce to x.atan2(y).
Math.atan2(2, 2) ~= pi/4;
Math.atan2(-2, 2) ~= -pi/4;
- cos(float)
Bounce to float.cos.
Math.cos(0) == 1;
Math.cos(pi) ~= -1;
- exp(float)
Bounce to float.exp.
- inf
Bounce to Float.inf.
- log(float)
Bounce to float.log.
- max(arg1, ...)
Bounce to [arg1, ...].max, see List.max.
max( 100, 20, 3 ) == 100;
max("100"
, "20"
, "3"
) == "3"
;
- min(arg1, ...)
Bounce to [arg1, ...].min, see List.min.
min( 100, 20, 3 ) == 3;
min("100"
, "20"
, "3"
) == "100"
;
- nan
Bounce to Float.nan.
- pi
Bounce to Float.pi.
- random(float)
Bounce to float.random.
- round(float)
Bounce to float.round.
Math.round(1) == 1;
Math.round(1.1) == 1;
Math.round(1.49) == 1;
Math.round(1.5) == 2;
Math.round(1.51) == 2;
- sign(float)
Bounce to float.sign.
Math.sign(2) == 1;
Math.sign(-2) == -1;
Math.sign(0) == 0;
- sin(float)
Bounce to float.sin.
Math.sin(0) == 0;
Math.sin(pi) ~= 0;
- sqr(float)
Bounce to float.sqr.
- sqrt(float)
Bounce to float.sqrt.
- srandom(float)
Bounce to float.srandom.
- tan(float)
Bounce to float.tan.
- trunc(float)
Bounce to float.trunc.
Math.trunc(1) == 1;
Math.trunc(1.1) == 1;
Math.trunc(1.49) == 1;
Math.trunc(1.5) == 1;
Math.trunc(1.51) == 1;
20.35 Mutex
Mutex allow to define critical sections.
A Mutex can be constructed like any other Tag but without name.
var m = Mutex.new;
[00000000] Mutex_0x964ed40
You can define critical sections by tagging your code using the Mutex.
var m = Mutex.new|;
m: echo("this is critical section"
);
[00000001] *** this is critical section
As a critical section, two pieces of code tagged by the same “Mutex” will never be executed at the
same time.
- asMutex
Return this.
var m1 = Mutex.new|;
assert
{
m1.asMutex === m1;
};
20.36 nil
The special entity nil is an object used to denote an empty value. Contrary to void
(??sec:std-void), it is a regular value which can be read.
Being a singleton, nil is not to be constructed, just used.
- isNil
Whether this is nil. I.e., true. See also Object.isNil.
nil.isNil;
!Object.isNil;
20.37 Object
All objects in urbiscript must have Object (??sec:std-Object) in their parents. Object
(??sec:std-Object) is done for this purpose so that it come with many primitives that are mandatory
for all object in urbiscript.
Fresh object can be instantiated by cloning Object itself.
Object.new;
[00000421] Object_0x00000000
The keyword class also allows to define objects which are intended to serve as prototype of a
family of objects, similarly to classes in traditional object-oriented programming languages (see
Section 9.4).
{
class Foo
{
var attr = 23;
};
assert
{
Foo.localSlotNames == ["asFoo"
, "attr"
, "type"
];
Foo.asFoo === Foo;
Foo.attr == 23;
Foo.type == "Foo"
;
};
};
20.38 Orderable
Objects that have a concept of “less than”. See also Comparable (??sec:std-Comparable).
This object, made to serve as prototype, provides a definition of < based on >, and vice versa; and
definition of <=/>= based on </>==. You must define either < or >, otherwise invoking either method
will result in endless recursions.
class Foo : Orderable
{
var value = 0;
function init (v) { value = v; };
function ’<’ (lhs) { value < lhs.value; };
function asString() { "<"
+ value.asString + ">"
; };
};
[00000000] <0>
var one = Foo.new(1);
[00000001] <1>
var two = Foo.new(2);
[00000002] <2>
assert( (one <= one) && (one <= two) && !(two <= one));
assert(!(one > one) && !(one > two) && (two > one));
assert( (one >= one) && !(one >= two) && (two >= one));
20.39 OutputStream
OutputStreams are used to write (possibly binary) files by hand.
An OutputStream is a writing-interface to a file; its constructor requires a File (??sec:std-File). If the
file already exists, content is appended to it. Remove the file beforehand if you want to override its
content.
System.system("(echo 1; echo 2) >file.txt"
)|;
OutputStream.new(File.new("file.txt"
));
[00000001] OutputStream_0x827000
OutputStream.new(File.create("new.txt"
));
[00000001] OutputStream_0x827000
- Output this.asString. Return this to enable chains of calls.
{
{
var o = OutputStream.new(File.create("fresh.txt"
));
o << 1 << "2"
<< [3, [4]];
};
File.new("fresh.txt"
).content.data;
}
==
"12[3, [4]]"
;
- close
Flush the buffers, and close the file.
OutputStream.new(File.create("file.txt"
)).close.isVoid;
- putByte(byte)
In order to private efficient input/output operations, buffers are used. As a consequence, what is
put into a stream might not be immediately saved on the actual file. To flush a buffer means to
dump its content to the file.
OutputStream.new(File.create("file.txt"
)).flush.isVoid;
20.40 Pair
A pair is a container storing two objects, similar in spirit to std::pair in C ++.
A Pair is constructed with two arguments.
Pair.new(1, 2);
[00000001] (1, 2)
Pair.new;
[00000003:error] !!! Pair.init: expected 2 arguments, given 0
Pair.new(1, 2, 3, 4);
[00000003:error] !!! Pair.init: expected 2 arguments, given 4
- first
Return the first member of the pair.
Pair.new(1, 2).first == 1;
Pair[0] === Pair.first;
- second
Return the second member of the pair.
Pair.new(1, 2).second == 2;
Pair[1] === Pair.second;
20.41 Path
A Path points to a file system entity (directory, file and so forth).
A Path is constructed with the string that points to the file system entity. This path can be relative or
absolute.
Path.new("/path/file.u"
);
[00000001] Path("/path/file.u")
Some minor simplifications are made, such as stripping useless ‘./’ occurrences.
Path.new("././///.//foo/"
);
[00000002] Path("foo")
- absolute
Whether this is absolute.
Path.new("/abs/path"
).absolute;
!Path.new("rel/path"
).absolute;
- asList
List of names used in path (directories and possibly file), from bottom up. There is no difference
between relative path and absolute path.
Path.new("/path/to/file.u"
).asList == ["path"
, "to"
, "file.u"
];
Path.new("/path"
).asList == Path.new("path"
).asList;
- asPrintable
Path.new("file.txt"
).asPrintable == "Path(\"file.txt\")"
;
- asString
The name of the file.
Path.new("file.txt"
).asString == "file.txt"
;
- basename
Base name of the path.
Path.new("/absolute/path/file.u"
).basename == "file.u"
;
Path.new("relative/path/file.u"
).basename == "file.u"
;
- cd
Change current working directory to this. Return the new current working directory as a
Path.
- cwd
The current working directory.
{
var pwd = Path.cwd|
var root = Path.new("/"
).cd;
assert(Path.cwd == root);
assert(pwd.cd == pwd);
};
- dirname
Directory name of the path.
Path.new("/abs/path/file.u"
).dirname == Path.new("/abs/path"
);
Path.new("rel/path/file.u"
).dirname == Path.new("rel/path"
);
- exists
Whether something (a file, a directory, …) exists where this points to.
Path.cwd.exists;
Path.new("/"
).exists;
!Path.new("/this/path/does/not/exists"
).exists;
- isDir
Whether this is a directory.
- isReg
Whether this is a regular file.
- open
Open this. Return either a Directory or a File according the type of this. See File
(??sec:std-File) and Directory (??sec:std-Directory).
- readable
Whether this is readable. Throw if does not even exist.
- writable
Whether this is writable. Throw if does not even exist.
- ’/’(rhs)
Create a new Path that is the concatenation of this and rhs. rhs can be a Path or a String and
cannot be absolute.
assert(Path.new("/foo/bar"
) / Path.new("baz/qux/quux"
)
== Path.new("/foo/bar/baz/qux/quux"
));
Path.cwd / Path.new("/tmp/foo"
);
[00000003:error] !!! /: Rhs of concatenation is absolute: /tmp/foo
- ’==’(that)
Same as comparing the string versions of this and that. Beware that two paths may be different
and point to the very same location.
Path.new("/a"
) == Path.new("/a"
);
!(Path.new("/a"
) == Path.new("a"
) );
- ’<’(that)
Same as comparing the string versions of this and that.
Path.new("/a"
) < Path.new("/a/b"
);
!(Path.new("/a/b"
) < Path.new("/a"
) );
20.42 Pattern
Pattern class is used to make correspondences between a pattern and another Object. The visit is
done either on the pattern or on the element against which the pattern is compared.
Patterns are used for the implementation of the pattern matching. So any class made compatible
with the pattern matching implemented by this class will allow you to use it implicitly in your
scripts.
[1, var a, var b] = [1, 2, 3];
[00000000] [1, 2, 3]
a;
[00000000] 2
b;
[00000000] 3
A Pattern can be created with any object that can be matched.
Pattern.new([1]);
[00000000] Pattern_0x189ea80
Pattern.new(Pattern.Binding.new("a"
));
[00000000] Pattern_0x18d98b0
- Binding
A class used to create pattern variables.
Pattern.Binding.new("a"
);
[00000000] var a
- bindings
A Dictionary filled by the match function for each Binding contained inside the
pattern.
{
var p = Pattern.new([Pattern.Binding.new("a"
), Pattern.Binding.new("b"
)]);
assert (p.match([1, 2]));
p.bindings
};
[00000000] ["a" => 1, "b" => 2]
- match(value)
Use value to unify the current pattern with this value. Return the status of the match.
- If the match is correct, then the bindings member will contain the result of every
matched values.
- If the match is incorrect, then the bindings member should not be used.
If the pattern contains multiple Binding with the same name, then the behavior is
undefined.
Pattern.new(1).match(1);
Pattern.new([1, 2]).match([1, 2]);
! Pattern.new([1, 2]).match([1, 3]);
! Pattern.new([1, 2]).match([1, 2, 3]);
Pattern.new(Pattern.Binding.new("a"
)).match(0);
Pattern.new([1, Pattern.Binding.new("a"
)]).match([1, 2]);
! Pattern.new([1, Pattern.Binding.new("a"
)]).match(0);
- matchPattern(pattern, value)
This function is used as a callback function to store all bindings in the same place. This function
is useful inside objects that implement a match or matchAgainst function that need to continue
the match deeper. Return the status of the match (a Boolean).
The pattern should provide a method match(handler,value) otherwise the value method
matchAgainst(handler, pattern) is used. If none are provided the ’==’ operator is
used.
To see how to use it, you can have a look at the implementation of List.matchAgainst.
- pattern
The pattern given at the creation.
Pattern.new(1).pattern == 1;
Pattern.new([1, 2]).pattern == [1, 2];
{
var pattern = [1, Pattern.Binding.new("a"
)];
Pattern.new(pattern).pattern === pattern
};
20.43 Position
This class is used to handle file locations with a line, column and file name.
Without argument, a newly constructed Position has its fields initialized to the first line and the first
column.
Position.new;
[00000001] 1.1
With a position argument p, the newly constructed Position is a clone of p.
Position.new(Position.new(2, 3));
[00000001] 2.3
With two float arguments l and c, the newly constructed Position has its line and column defined
and an empty file name.
Position.new(2, 3);
[00000001] 2.3
With three arguments f, l and c, the newly constructed Position has its file name, line and column
defined.
Position.new("file.u"
, 2, 3);
[00000001] file.u:2.3
- ’+’(n)
Return a new Position which is shifted from n columns to the right. The minimal value of the
new position column is 1.
Position.new(2, 3) + 2 == Position.new(2, 5);
Position.new(2, 3) + -4 == Position.new(2, 1);
- ’-’(n)
Return a new Position which is shifted from n columns to the left. The minimal value of the new
Position column is 1.
Position.new(2, 3) - 1 == Position.new(2, 2);
Position.new(2, 3) - -4 == Position.new(2, 7);
- ’==’(other)
Compare the lines and columns of two Positions.
Position.new(2, 3) == Position.new(2, 3);
Position.new("a.u"
, 2, 3) == Position.new("b.u"
, 2, 3);
Position.new(2, 3) != Position.new(2, 2);
- ’<’(other)
Order comparison of lines and columns.
Position.new(2, 3) < Position.new(2, 4);
Position.new(2, 3) < Position.new(3, 1);
- asString
Present as ‘file:line.column ’, the file name is omitted if it is not defined.
Position.new("file.u"
, 2, 3);
[00000001] file.u:2.3
- column
Field which give access to the column number of the Position.
Position.new(2, 3).column == 3;
- columns(n)
Identical to ’+’(n).
Position.new(2, 3).columns(2) == Position.new(2, 5);
Position.new(2, 3).columns(-4) == Position.new(2, 1);
- file
The Path of the Position file.
Position.new("file.u"
, 2, 3).file == Path.new("file.u"
);
Position.new(2, 3).file == nil;
- line
Field which give access to the line number of the Position.
Position.new(2, 3).line == 2;
- lines(n)
Add n lines and reset the column number to 1.
Position.new(2, 3).lines(2) == Position.new(4, 1);
Position.new(2, 3).lines(-1) == Position.new(1, 1);
20.44 Primitive
C++ routine callable from urbiscript.
It is not possible to construct a Primitive.
- apply(args)
Invoke a primitive. The argument list, args, must start with the target.
Float.getSlot("+"
).isA(Global.getSlot("Primitive"
));
Float.getSlot("+"
).apply([1, 2]) == 3;
String.getSlot("+"
).isA(Global.getSlot("Primitive"
));
String.getSlot("+"
).apply(["1"
, "2"
]);
- asPrimitive
Return this.
Float.getSlot("+"
).asPrimitive === Float.getSlot("+"
);
20.45 Process
A Process is a separated task handled by the underneath operating system.
Windows Issues
Process is not yet supported under Windows.
The following examples runs the cat program, a Unix standard command that simply copies on its
(standard) output its (standard) input.
var p = Process.new("cat"
, []);
[00000004] Process cat
Just created, this process is not running yet. Use run to launch it.
p.status;
[00000005] not started
p.run;
p.status;
[00000006] running
Then we feed its input, named stdin in the Unix tradition, and close its input.
p.stdin << "1\n"
|
p.stdin << "2\n"
|
p.stdin << "3\n"
|;
p.status;
[00000007] running
p.stdin.close;
At this stage, the status of the process is unknown, as it is running asynchronously. If it has had
enough time to “see” that its input is closed, then it will have finished, otherwise we might have to
wait for awhile. The method join means “wait for the process to finish”.
p.join;
p.status;
[00000008] exited with status 0
Finally we can check its output.
p.stdout.asList;
[00000009] ["1", "2", "3"]
A Process needs a program name to run and a possibly-empty list of command line arguments. Calling
run is required to execute the process.
Process.new("cat"
, []);
[00000004] Process cat
Process.new("cat"
, ["--version"
]);
[00000004] Process cat
- asProcess
Return this.
do (Process.new("cat"
, []))
{
assert (asProcess === this);
}|;
- asString
Process and the name of the program.
Process.new("cat"
, ["--version"
]).asString
== "Process cat"
;
- done
Whether the process has completed its execution.
do (Process.new("sleep"
, ["1"
]))
{
assert (!done);
run;
assert (!done);
join;
assert (done);
}|;
- join
Wait for the process to finish. Changes its status.
do (Process.new("sleep"
, ["2"
]))
{
var t0 = System.time;
assert (status.asString == "not started"
);
run;
assert (status.asString == "running"
);
join;
assert (t0 + 2s <= System.time);
assert (status.asString == "exited with status 0"
);
}|;
- kill
If the process is not done, interrupt it (with a SIGKILL in Unix parlance). You still have to wait
for its termination with join.
do (Process.new("sleep"
, ["1"
]))
{
run;
kill;
join;
assert (done);
assert (status.asString == "killed by signal 9"
);
}|;
- name
The (base) name of the program the process runs.
Process.new("cat"
, ["--version"
]).name == "cat"
;
- run
Launch the process. Changes it status. A process can only be run once.
do (Process.new("sleep"
, ["1"
]))
{
assert (status.asString == "not started"
);
run;
assert (status.asString == "running"
);
join;
assert (status.asString == "exited with status 0"
);
run;
}|;
[00021972:error] !!! run: Process was already run
- runTo
- status
An object whose slots describe the status of the process.
- stderr
An InputStream (??sec:std-InputStream) (the output of the Process is an input for Urbi) to the
standard error stream of the process.
do (Process.new(System.programName, ["--"
, "--no-such-option"
]))
{
run;
join;
assert
{
stderr.asList ==
[System.programName + ": invalid option: --no-such-option"
,
"Try ‘"
+ System.programName + " --help’ for more information."
];
};
}|;
- stdin
An OutputStream (??sec:std-OutputStream) (the input of the Process is an output for Urbi) to
the standard input stream of the process.
do (Process.new(System.programName, ["--version"
]))
{
run;
join;
assert
{
stdout.asList[1] == "Copyright (C) 2004-2010 Gostai S.A.S.."
;
};
}|;
- stdout
An InputStream (??sec:std-InputStream) (the output of the Process is an input for Urbi) to the
standard output stream of the process.
do (Process.new("cat"
, []))
{
run;
stdin << "Hello, World!\n"
;
stdin.close;
join;
assert (stdout.asList == ["Hello, World!"
]);
}|;
20.46 Profiling
Profiling is useful to get an idea of the efficiency of some small pieces of code.
A Profiling can be created with two arguments. The first argument is the expression which has to be
profiled and the second is the number of iteration it should be run.
Creating a Profiling session prints the result of the profiled expression, the number of iterations,
the number of cycles and the time of the evaluation. The number of cycles corresponds to the number
of time the job is scheduled.
Profiling.new({1| 2| 3| 4}, 10000);
[00000000] Profiling information
Expression: 1 | 2 | 3 | 4
Iterations: 10000
Cycles: 10000
Total time: 1.00098 s
Single iteration: 0.000100098 s
1 cycles
Profiling.new({1; 2; 3; 4}, 10000);
[00000000] Profiling information
Expression: 1;
2;
3;
4
Iterations: 10000
Cycles: 40000
Total time: 1.45856 s
Single iteration: 0.000145856 s
4 cycles
20.47 PseudoLazy
20.48 PubSub
PubSub provides an abstraction over Barrier Barrier (??sec:std-Barrier) to queue signals for each
subscriber.
A PubSub can be created with no arguments. Values can be published and read by each
subscriber.
var ps = PubSub.new;
[00000000] PubSub_0x28c1bc0
- publish(ev)
Queue the value ev to the queue of each subscriber. This method returns the value ev.
{
var sub = ps.subscribe;
assert
{
ps.publish(2) == 2;
sub.getOne == 2;
};
ps.unsubscribe(sub)
}|;
- subscribe
Create a Subscriber and insert it inside the list of subscribers.
var sub = ps.subscribe |
ps.subscribers == [sub];
[00000000] true
- Subscriber
See PubSub.Subscriber (??sec:std-PubSub.Subscriber).
- subscribers
Field containing the list of Subscriber which are watching published values. This field only
exists in instances of PubSub.
- unsubscribe(sub)
Remove a subscriber from the list of subscriber watching the published values.
ps.unsubscribe(sub) |
ps.subscribers;
[00000000] []
20.49 PubSub.Subscriber
Subscriber is created by PubSub.subscribe. It provides methods to access to the list of values
published by PubSub instances.
A PubSub.Subscriber can be created with a call to PubSub.subscribe. This way of creating a
Subscriber adds the subscriber as a watcher of values published on the instance of PubSub.
var ps = PubSub.new |;
var sub = ps.subscribe;
[00000000] Subscriber_0x28607c0
- getOne
Block until a value is accessible and return it. If a value is already queued, then the method
returns it without blocking.
echo(sub.getOne) &
ps.publish(3);
[00000000] *** 3
- getAll
Block until a value is accessible. Return the list of queued values. If the values are already
queued, then return them without blocking.
ps.publish(4) |
ps.publish(5) |
echo(sub.getAll);
[00000000] *** [4, 5]
20.50 RangeIterable
This object is meant to be used as a prototype for objects that support an asList method, to use
range-based for loops (Section 19.6.5.2).
- all(fun)
Return whether all the members of the target verify the predicate fun.
! [-2, 0, 2, 4].all(function (e) { e > 0 });
[-2, 0, 2, 4].all(function (e) { e % 2 == 0 });
- any(fun)
Whether at least one of the members of the target verifies the predicate fun.
! [-3, 1, -1].any(function (e) { e % 2 == 0 });
[-3, 1, -1].any(function (e) { e > 0 });
- each(fun)
Apply the given functional value fun on all “members”, sequentially. Corresponds to range-for
loops.
class range : RangeIterable
{
var asList = [10, 20, 30];
}|;
for (var i : range)
echo (i);
[00000000] *** 10
[00000000] *** 20
[00000000] *** 30
- ’each&’(fun)
Apply the given functional value fun on all “members”, in parallel, starting all the computations
simultaneously. Corresponds to range-for& loops.
{
var res = [];
for& (var i : range)
res << i;
assert(res.sort == [10, 20, 30]);
};
- ’each|’(fun)
Apply the given functional value fun on all “members”, with tight sequentially. Corresponds to
range-for| loops.
{
var res = [];
for| (var i : range)
res << i;
assert(res == [10, 20, 30]);
};
20.51 Regexp
A Regexp is an object which allow you to match strings with a regular expression.
A Regexp is created with the regular expression once and for all, and it can be used many times to
match with other strings.
Regexp.new("."
);
[00000001] Regexp(".")
urbiscript supports Perl regular expressions, see the perlre man page. Expressions cannot be
empty.
- asPrintable
A string that shows that this is a Regexp, and its value.
Regexp.new("abc"
).asPrintable == "Regexp(\"abc\")"
;
Regexp.new("\\d+(\\.\\d+)?"
).asPrintable == "Regexp(\"\\\\d+(\\\\.\\\\d+)?\")"
;
- asString
The regular expression that was compiled.
Regexp.new("abc"
).asString == "abc"
;
Regexp.new("\\d+(\\.\\d+)?"
).asString == "\\d+(\\.\\d+)?"
;
- has(str)
An experimental alias to match, so that the infix operators in and not in can be used (see
Section 19.1.8.6).
"23.03"
in Regexp.new("^\\d+\\.\\d+$"
);
"-3.14"
not in Regexp.new("^\\d+\\.\\d+$"
);
- match(str)
Whether this matches str.
var r = Regexp.new("oo"
)|
assert
{
r.match("oo"
);
r.match("foobar"
);
!r.match("bazquux"
);
};
r = Regexp.new("^oo"
)|
assert
{
r.match("oops"
);
!r.match("woot"
);
};
r = Regexp.new("oo$"
)|
assert
{
r.match("foo"
);
!r.match("mooh"
);
};
r = Regexp.new("fo*bar"
)|
assert
{
r.match("fbar"
);
r.match("fooooobar"
);
!r.match("far"
);
};
r = Regexp.new("f(oo)*bar"
)|
assert
{
r.match("foooobar"
);
!r.match("fooobar"
);
};
20.52 StackFrame
This class is meant to record backtrace (see Exception.backtrace) information.
For convenience, all snippets of code are supposed to be run after these function definitions. In this
code, the getStackFrame function is used to get the first StackFrame of an exception backtrace.
Backtrace of Exception (??sec:std-Exception) are filled with StackFrames when the is
thrown.
function inner () { throw Exception.new("test"
) }|;
function getStackFrame()
{
try
{
inner
}
catch(var e)
{
e.backtrace[0]
};
}|;
This feature is experimental. It might be changed in the future. Feedback on its use
would be appreciated.
StackFrame are not made to be manually constructed. The initialization function expect 2 arguments,
which are the name of the called function and the Location (??sec:std-Location) from which it has
been called.
StackFrame.new("inner"
,
Location.new(
Position.new("foo.u"
, 7, 5),
Position.new("foo.u"
, 7, 10)
)
);
[00000001] foo.u:7.5-9: inner
- name
String (??sec:std-String), representing the name of the called function.
getStackFrame.name;
[00000002] "inner"
- location
Location (??sec:std-Location) of the function call.
getStackFrame.location;
[00000003] foo.u:7.5-9
- asString
Clean display of the call location.
getStackFrame;
[00000004] foo.u:7.5-9: inner
20.53 Semaphore
Semaphore are useful to limit the number of access to a limited number of resources.
A Semaphore can be created with as argument the number of processes allowed to enter critical
sections at the same time.
Semaphore.new(1);
[00000000] Semaphore_0x8c1e80
- criticalSection(function ()
¡code¿)Put the piece of code inside a critical section which can be executed simultaneously
at most the number of time given at the creation of the Semaphore. This method is similar
to a call to acquire and a call to release when the code ends by any means.
{
var s = Semaphore.new(1);
for& (var i : [0, 1, 2, 3])
{
s.criticalSection(function () {
echo("start "
+ i);
echo("end "
+ i);
})
}
};
[00000000] *** start 0
[00000000] *** end 0
[00000000] *** start 1
[00000000] *** end 1
[00000000] *** start 2
[00000000] *** end 2
[00000000] *** start 3
[00000000] *** end 3
{
var s = Semaphore.new(2);
for& (var i : [0, 1, 2, 3])
{
s.criticalSection(function () {
echo("start "
+ i);
sleep(i * 100ms);
echo("end "
+ i);
})
}
};
[00000000] *** start 0
[00000000] *** start 1
[00000000] *** end 0
[00000000] *** start 2
[00000000] *** end 1
[00000000] *** start 3
[00000000] *** end 2
[00000000] *** end 3
- acquire
Wait to enter a critical section delimited by the execution of acquire and release. Enter
the critical section when the number of processes inside it goes below the maximum
allowed.
- p
Historical synonym for acquire.
- release
Leave a critical section delimited by the execution of acquire and release.
{
var s = Semaphore.new(1);
for& (var i : [0, 1, 2, 3])
{
s.acquire;
echo("start "
+ i);
echo("end "
+ i);
s.release;
}
};
[00000000] *** start 0
[00000000] *** end 0
[00000000] *** start 1
[00000000] *** end 1
[00000000] *** start 2
[00000000] *** end 2
[00000000] *** start 3
[00000000] *** end 3
- v
Historical synonym for release.
20.54 Server
A Server can listen to incoming connections. See Socket (??sec:std-Socket) for an example.
A Server is constructed with no argument. At creation, a new Server has its own slot connection.
This slot is an event that is launched when a connection establishes.
var s = Server.new|
s.localSlotNames;
[00000001] ["connection"]
- connection
The event launched at each incoming connection. This event is launched with one argument:
the socket of the established connection. This connection uses the same IoService
(??sec:std-IoService) as the server.
at (s.connection?(var socket))
{
};
- getIoService
Return the IoService (??sec:std-IoService) used by this socket. Only the default IoService is
automatically polled.
- host
The host on which this is listening. Raise an error if this is not listening.
Server.host;
[00000003:error] !!! host: server not listening
- listen(host, port)
Listen incoming connections with host and port.
- port
The port on which this is listening. Raise an error if this is not listening.
Server.port;
[00000004:error] !!! port: server not listening
- sockets
The list of the sockets created at each incoming connection.
20.55 Singleton
A singleton is a prototype that cannot be cloned. All prototypes derived of Singleton are also
singletons.
To be a singleton, the object must have Singleton as a prototype. The common way to do this is
var s = Singleton.new, but this does not work : s is not a new singleton, it is the Singleton itself
since it cannot be cloned. There are two other ways:
class NewSingleton1: Singleton
{
var asString = "NewSingleton1"
;
}|
var s1 = NewSingleton1.new;
[00000001] NewSingleton1
assert(s1 === NewSingleton1);
assert(NewSingleton1 !== Singleton);
var NewSingleton2 = Object.new|
var NewSingleton2.asString = "NewSingleton2"
|
NewSingleton2.protos = [Singleton]|
var s2 = NewSingleton2.new;
[00000001] NewSingleton2
assert(s2 === NewSingleton2);
assert(NewSingleton2 !== Singleton);
- clone
Return this.
- ’new’
Return this.
20.56 Socket
A Socket can manage asynchronous input/output network connections.
The following example demonstrates how both the Server (??sec:std-Server) and Socket
(??sec:std-Socket) object work.
This simple example will establish a dialog between server and client. The following
object, Dialog, contains the script of this exchange. It is put into Global so that both
the server and client can read it. Dialog.reply(var s) returns the reply to a message
s.
class Global.Dialog
{
var lines =
[
"Hi!"
,
"Hey!"
,
"Hey you doin’?"
,
"Whazaaa!"
,
"See ya."
,
]|;
function reply(var s)
{
for (var i: lines.size - 1)
if (s == lines[i])
return lines[i + 1];
"off"
;
}
}|;
The server, an instance of Server (??sec:std-Server), expects incoming connections,
notified by the socket’s connection? event. Once the connection establish, it listens to
the socket for incoming messages, notified by the received? event. Its reaction to this
event is to send the following line of the dialog. At the end of the dialog, the socket is
disconnected.
var server =
do (Server.new)
{
at (connection?(var socket))
at (socket.received?(var data))
{
var reply = Dialog.reply(data);
socket.write(reply);
echo("server: "
+ reply);
if (reply == "off"
)
socket.disconnect;
};
}|;
The client, an instance of Socket (??sec:std-Socket) expects incoming messages, notified by the
received? event. Its reaction is to send the following line of the dialog.
var client =
do (Socket.new)
{
at (received?(var data))
{
var reply = Dialog.reply(data);
write(reply);
echo("client: "
+ reply);
};
}|;
As of today, urbiscript’s socket machinery requires to be regularly polled.
every (100ms)
Socket.poll,
The server is then activated, listening to incoming connections on a port that will be chosen by the
system amongst the free ones.
server.listen("localhost"
, "0"
);
clog << "connecting to %s:%s"
% [server.host, server.port];
The client connects to the server, and initiates the dialog.
client.connect(server.host, server.port);
echo("client: "
+ Dialog.lines[0]);
client.write(Dialog.lines[0]);
[00000003] *** client: Hi!
Because this dialog is asynchronous, the easiest way to wait for the dialog to finish is to wait for
the disconnected? event.
waituntil(client.disconnected?);
[00000004] *** server: Hey!
[00000005] *** client: Hey you doin’?
[00000006] *** server: Whazaaa!
[00000007] *** client: See ya.
[00000008] *** server: off
A Socket is constructed with no argument. At creation, a new Socket has four own slots: connected,
disconnected, error and received.
- connect(host, port)
Connect this to host and port. The port can be either an integer, or a string that denotes
symbolic ports, such as "smtp", or "ftp" and so forth.
- connected
Event launched when the connection is established.
- connectSerial(device, baudRate)
Connect this to the serial port device, with given baudRate.
- disconnect
Close the connection.
- disconnected
Event launched when a disconnection happens.
- error
Event launched when an error happens. This event is launched with the error message in
argument. The event disconnected is also always launched.
- getIoService
Return the IoService (??sec:std-IoService) used by this socket. Only the default
IoService is automatically polled.
- host
The remote host of the connection.
- isConnected
Whether this is connected.
- localHost
The local host of the connection.
- localPort
The local port of the connection.
- poll
Call getIoService.poll(). This method is called regularly every pollInterval on the
Socket object. You do not need to call this function on your sockets unless you use your
own IoService (??sec:std-IoService).
- pollInterval
Each pollInterval amount of time, poll is called. If pollInterval equals zero, poll is
not called.
- port
The remote port of the connection.
- received
Event launched when this has received data. The data is given by argument to the event.
- write(data)
Sends data trough the connection.
- syncWrite(data)
Similar to write, but forces the operation to complete synchronously. Synchronous and
asynchronous write operations cannot be mixed.
20.57 String
A string is a sequence of characters.
Fresh Strings can easily be built using the literal syntax. Several escaping sequences (the traditional
ones and urbiscript specific ones) allow to insert special characters. Consecutive string literals are
merged together. See Section 19.1.6.6 for details and examples.
A null String can also be obtained with String’s new method.
String.new == ""
;
String == ""
;
"123"
.new == "123"
;
- asFloat
If the whole content of this is an integer, return its value, otherwise return an error.
assert("23.03"
.asFloat == 23.03);
"123abc"
.asFloat;
[00000001:error] !!! asFloat: unable to convert to float: "123abc"
- asList
Return a List of one-letter Strings that, concatenated, equal this. This allows to use for to
iterate over the string.
assert("123"
.asList == ["1"
, "2"
, "3"
]);
for (var v : "123"
)
echo(v);
[00000001] *** 1
[00000001] *** 2
[00000001] *** 3
- asPrintable
Return this as a literal (escaped) string.
"foo"
.asPrintable == "\"foo\""
;
"foo"
.asPrintable.asPrintable == "\"\\\"foo\\\"\""
;
- asString
Return this.
"\"foo\""
.asString == "\"foo\""
;
- closest(set)
Return the string in set that is the closest (in the sense of distance) to this. If there is no
convincing match, return nil.
"foo"
.closest(["foo"
, "baz"
, "qux"
, "quux"
]) == "foo"
;
"bar"
.closest(["foo"
, "baz"
, "qux"
, "quux"
]) == "baz"
;
"FOO"
.closest(["foo"
, "bar"
, "baz"
]) == "foo"
;
"qux"
.closest(["foo"
, "bar"
, "baz"
]) == nil;
- distance(other)
Return the Damerau-Levenshtein distance between this and other. The more alike the strings
are, the smaller the distance is.
"foo"
.distance("foo"
) == 0;
"bar"
.distance("baz"
) == 1;
"foo"
.distance("bar"
) == 3;
- fresh
Return a String that has never been used as an identifier, prefixed by this. It can safely be used
with Object.setSlot and so forth.
String.fresh == "_5"
;
"foo"
.fresh == "foo_6"
;
- Character handling functions
Here is a map of how the original 127-character ASCII set is considered by each function (a ∙
indicates that the function returns true if all characters of this are on the row).
|
|
|
|
|
|
|
|
|
|
|
|
|
| | | | | | | | | | | | | |
| ASCII values | Characters | iscntrl | isspace | isupper | islower | isalpha | isdigit | isxdigit | isalnum | ispunct | isgraph | print |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x00 .. 0x08 | | ∙ | | | | | | | | | | |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x09 .. 0x0D | \t, \f, \v, \n, \r | ∙ | ∙ | | | | | | | | | |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x0E .. 0x1F | | ∙ | | | | | | | | | | |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x20 | space (’ ’) | | ∙ | | | | | | | | | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x21 .. 0x2F | !"#$%&’()*+,-./ | | | | | | | | | ∙ | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x30 .. 0x39 | 0-9 | | | | | | ∙ | ∙ | ∙ | | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x3a .. 0x40 | :;<=>?@ | | | | | | | | | ∙ | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x41 .. 0x46 | A-F | | | ∙ | | ∙ | | ∙ | ∙ | | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x47 .. 0x5A | G-Z | | | ∙ | | ∙ | | | ∙ | | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x5B .. 0x60 | [\]^{}_‘ | | | | | | | | | ∙ | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x61 .. 0x66 | a-f | | | | ∙ | ∙ | | ∙ | ∙ | | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x67 .. 0x7A | g-z | | | | ∙ | ∙ | | | ∙ | | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x7B .. 0x7E | {|}~ | | | | | | | | | ∙ | ∙ | ∙ |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 0x7F | (DEL) | ∙ | | | | | | | | | | |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
""
.isDigit;
"0123456789"
.isDigit;
!"a"
.isDigit;
""
.isLower;
"lower"
.isLower;
! "Not Lower"
.isLower;
""
.isUpper;
"UPPER"
.isUpper;
! "Not Upper"
.isUpper;
- join(list, prefix, suffix)
Glue the result of asString applied to the members of list, separated by this, and embedded
in a pair prefix/suffix.
"|"
.join([1, 2, 3], "(", ")"
) == "(1|2|3)"
;
", "
.join([1, [2], "3"
], "["
, "]"
) == "[1, [2], 3]"
;
- replace(from, to)
Replace every occurrence of the string from in this by to, and return the result. this is not
modified.
"Hello, World!"
.replace("Hello"
, "Bonjour"
)
.replace("World!"
, "Monde !"
) ==
"Bonjour, Monde !"
;
- size
Return the size of the string.
"foo"
.size == 3;
""
.size == 0;
- split(sep = [" ", "\t", "\n", "\r"], lim = -1, keepSep = false, keepEmpty = true)
Split this on the separator sep, in at most lim components, which include the separator if
keepSep, and the empty components of keepEmpty. Return a list of strings.
The separator, sep, can be a string.
"a,b;c"
.split(","
) == ["a"
, "b;c"
];
"a,b;c"
.split(";"
) == ["a,b"
, "c"
];
"foobar"
.split("x"
) == ["foobar"
];
"foobar"
.split("ob"
) == ["fo"
, "ar"
];
It can also be a list of strings.
"a,b;c"
.split([","
, ";"
]) == ["a"
, "b"
, "c"
];
By default splitting is performed on white-spaces:
" abc def\tghi\n"
.split == ["abc"
, "def"
, "ghi"
];
Splitting on the empty string stands for splitting between each character:
"foobar"
.split(""
) == ["f"
, "o"
, "o"
, "b"
, "a"
, "r"
];
The limit lim indicates a maximum number of splits that can occur. A negative number
corresponds to no limit:
"a:b:c"
.split(":"
, 1) == ["a"
, "b:c"
];
"a:b:c"
.split(":"
, -1) == ["a"
, "b"
, "c"
];
keepSep indicates whether to keep delimiters in the result:
"aaa:bbb;ccc"
.split([":"
, ";"
], -1, false) == ["aaa"
, "bbb"
, "ccc"
];
"aaa:bbb;ccc"
.split([":"
, ";"
], -1, true) == ["aaa"
, ":"
, "bbb"
, ";"
, "ccc"
];
keepEmpty indicates whether to keep empty elements:
"foobar"
.split("o"
) == ["f"
, ""
, "bar"
];
"foobar"
.split("o"
, -1, false, true) == ["f"
, ""
, "bar"
];
"foobar"
.split("o"
, -1, false, false) == ["f"
, "bar"
];
- toLower
Make lower case every upper case character in this and return the result. this is not modified.
"Hello, World!"
.toLower == "hello, world!"
;
- toUpper
Make upper case every lower case character in this and return the result. this is not modified.
"Hello, World!"
.toUpper == "HELLO, WORLD!"
;
- ’==’(that)
Whether this and that are the same string.
""
== ""
; !(""
!= ""
);
!(""
== "\0"
); ""
!= "\0"
;
"0"
== "0"
; !("0"
!= "0"
);
!("0"
== "1"
); "0"
!= "1"
;
!("1"
== "0"
); "1"
!= "0"
;
- ’%’(args)
It is an equivalent of Formatter.new(this) % args. See Formatter (??sec:std-Formatter).
"%s + %s = %s"
% [1, 2, 3] == "1 + 2 = 3"
;
- ’*’(n)
Concatenate this n times.
"foo"
* 0 == ""
;
"foo"
* 1 == "foo"
;
"foo"
* 3 == "foofoofoo"
;
- ’+’(other)
Concatenate this and other.asString.
"foo"
+ "bar"
== "foobar"
;
"foo"
+ ""
== "foo"
;
"foo"
+ 3 == "foo3"
;
"foo"
+ [1, 2, 3] == "foo[1, 2, 3]"
;
- ’<’(other)
Whether this is lexicographically before other, which must be a String.
""
< "a"
;
!("a"
< ""
);
"a"
< "b"
;
!("a"
< "a"
);
- ’[]’(from)
’[]’(from, to)
Return the sub-string starting at from, up to and not including to (which defaults to to + 1).
"foobar"
[0, 3] == "foo"
;
"foobar"
[0] == "f"
;
- ’[]=’(from, other)
’[]=’(from, to, other)
Replace the sub-string starting at from, up to and not including to (which defaults to to + 1),
by other. Return other.
Beware that this routine is imperative: it changes the value of this.
var s1 = "foobar"
| var s2 = s1 |
assert((s1[0, 3] = "quux"
) == "quux"
);
assert(s1 == "quuxbar"
);
assert(s2 == "quuxbar"
);
assert((s1[4, 7] = ""
) == ""
);
assert(s2 == "quux"
);
20.58 System
Details on the architecture the Urbi server runs on.
- _exit(status)
Shut the server down brutally: the connections are not closed, and the resources are not
explicitly released (the operating system reclaims most of them: memory, file descriptors
and so forth). Architecture dependent.
- aliveJobs
The number of detached routines currently running.
{
var nJobs = aliveJobs;
for (var i: [1s, 2s, 3s])
detach({sleep(i)});
sleep(0.5s);
assert(aliveJobs - nJobs == 3);
sleep(1s);
assert(aliveJobs - nJobs == 2);
sleep(1s);
assert(aliveJobs - nJobs == 1);
sleep(1s);
assert(aliveJobs - nJobs == 0);
};
- arguments
The list of the command line arguments passed to the user script. This is especially useful in
scripts.
$ cat >echo <<EOF
System.arguments;
shutdown;
EOF
$ chmod +x echo
$ ./echo 1 2 3
[00000172] ["1", "2", "3"]
$ ./echo -x 12 -v "foo"
[00000172] ["-x", "12", "-v", "foo"]
- ’assert’(assertion)
Unless ndebug is true, throw an error if assertion is not verified. See also the assertion support
in urbiscript, Section 19.8.
’assert’(true);
’assert’(42);
’assert’(1 == 1 + 1);
[00000002:error] !!! failed assertion: 1.’==’(1.’+’(1))
- assert_(assertion, message)
If assertion does not evaluate to true, throw the failure message.
assert_(true, "true failed"
);
assert_(42, "42 failed"
);
assert_(1 == 1 + 1, "one is not two"
);
[00000001:error] !!! failed assertion: one is not two
- assert_op(operator, lhs, rhs)
Deprecated, use assert instead, see Section 19.8.
- backtrace
Display the call stack on the channel backtrace.
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
function innermost () { backtrace }|;
function inner () { innermost }|;
function outer () { inner }|;
function outermost () { outer }|;
outermost;
[00000013:backtrace] innermost (foo.u:101.25-33)
[00000014:backtrace] inner (foo.u:102.25-29)
[00000015:backtrace] outer (foo.u:103.25-29)
[00000016:backtrace] outermost (foo.u:104.1-9)
- cycle
The number of execution cycles since the beginning.
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
{
var first = cycle ; var second = cycle ;
assert(first + 1 == second);
first = cycle | second = cycle ;
assert(first == second);
};
- eval(source)
Evaluate the urbiscript source, and return its result. The source must be complete, yet the
terminator (e.g., ‘;’) is not required.
eval("1+2"
) == 1+2;
eval("\"x\" * 10"
) == "x"
* 10;
eval("eval(\"1\")"
) == 1;
eval("{ var x = 1; x + x; }") == 2;
The evaluation is performed in the context of the current object (this), in particular, to create
local variables, create scopes.
eval("var x = 23;"
) == 23;
x == 23;
Exceptions are thrown on error.
eval("1/0"
);
[00008316:error] !!! 1.1-3: /: division by 0
try
{
eval ("1/0"
)
}
catch (var e)
{
assert
{
e.isA(Exception.Primitive);
e.location.asString == "1.1-3"
;
e.routine == "/"
;
e.message == "division by 0"
;
}
};
- getenv(name)
Return the value of the environment variable name as a String (??sec:std-String) if set, nil
(??sec:std-nil) otherwise. See also setenv and unsetenv.
getenv("UndefinedEnvironmentVariable"
).isNil;
!getenv("PATH"
).isNil;
- load(file)
Look for file in the Urbi path (Section 18.1), and load it. Throw a Exception.FileNotFound
error if the file cannot be found. Return the last value of the file.
System.system("echo ’123;’ >123.u"
) == 0;
load("123.u"
) == 123;
- loadFile(file)
Load the urbiscript file file. Throw a Exception.FileNotFound error if the file cannot be
found. Return the last value of the file.
System.system("echo ’123;’ >123.u"
) == 0;
loadFile("123.u"
) == 123;
- loadLibrary(library)
Load the library library, to be found in the URBI_UOBJECT_PATH search-path (see Section 18.1),
or the default UObject path. The library may be a String (??sec:std-String) or a Path
(??sec:std-Path). The C ++ symbols are made available to the other C ++ components. See also
loadModule.
- loadModule(module)
Load the UObject module. Same as loadLibrary, except that the low-level C ++ symbols are not
made “global” (in the sense of the shared library loader).
- lobbies
Bounce to Lobby.instances.
- lobby
Bounce to Lobby.lobby.
- maybeLoad(file, channel = Channel.null)
Look for file in the Urbi path (Section 18.1). If the file is found announce on Channel that
file is about to be loaded, and load it.
System.system("echo ’123;’ >123.u"
) == 0;
maybeLoad("123.u"
) == 123;
maybeLoad("u.123"
).isVoid;
- ndebug
If true, do not evaluate the assertions. See Section 19.8.
function one() { echo("called!"
); 1 }|;
assert(!System.ndebug);
assert(one);
[00000617] *** called!
System.ndebug = true|;
assert(one);
System.ndebug = false|;
assert(one);
[00000622] *** called!
- PackageInfo
See System.PackageInfo (??sec:std-System.PackageInfo).
- period
The period of the Urbi kernel. Influences the trajectories (TrajectoryGenerator
(??sec:std-TrajectoryGenerator)), and the UObject monitoring. Defaults to 20ms.
- Platform
See System.Platform (??sec:std-System.Platform).
- programName
The path under which the Urbi process was called. This is typically ‘.../urbi’ (Section 18.3) or
‘.../urbi-launch’ (Section 18.5).
Path.new(System.programName).basename
in ["urbi"
, "urbi.exe"
, "urbi-launch"
, "urbi-launch.exe"
];
- reboot
Restart the Urbi server. Architecture dependent.
- redefinitionMode
Switch the current job in redefinition mode until the end of the current scope. While in
redefinition mode, setSlot on already existing slots will overwrite the slot instead of
erring.
var Global.x = 0;
[00000001] 0
{
System.redefinitionMode;
var Global.x = 1;
echo(Global.x);
};
[00000002] *** 1
var Global.x = 0;
[00000003:error] !!! slot redefinition: x
- scopeTag
Bounce to Tag.scope.
- searchFile(file)
Look for file in the Urbi path (Section 18.1) and return its Path (??sec:std-Path). Throw a
Exception.FileNotFound error if the file cannot be found.
System.system("echo ’123;’ >123.u"
) == 0;
searchFile("123.u"
) == Path.cwd / Path.new("123.u"
);
- setenv(name, value)
Set the environment variable name to value.asString, and return this value. See also getenv
and unsetenv.
Windows Issues
Under Windows, setting to an empty value is equivalent to making undefined.
setenv("MyVar"
, 12) == "12"
;
getenv("MyVar"
) == "12"
;
System.system("exit $MyVar"
) >> 8 ==
{if (Platform.isWindows) 0 else 12};
setenv("MyVar"
, 23) == "23"
;
System.system("exit $MyVar"
) >> 8 ==
{if (Platform.isWindows) 0 else 23};
setenv("MyVar"
, ""
) == ""
;
getenv("MyVar"
).isNil == Platform.isWindows;
- shiftedTime
Return the number of seconds elapsed since the Urbi server was launched. Contrary to time,
time spent in frozen code is not counted.
{ var t0 = shiftedTime | sleep(1s) | shiftedTime - t0 }.round ~= 1;
1 ==
{
var t = Tag.new|;
var t0 = time|;
var res;
t: { sleep(1s) | res = shiftedTime - t0 },
t.freeze;
sleep(1s);
t.unfreeze;
sleep(1s);
res.round;
};
- shutdown
Have the Urbi server shut down. All the connections are closed, the resources are released.
Architecture dependent.
- sleep(duration)
Suspend the execution for duration seconds. No CPU cycle is wasted during this
wait.
(time - {sleep(1s); time}).round == -1;
- spawn(function, clear)
Run the function, with fresh tags if clear is true, otherwise under the control of the current
tags. Return the spawn Job (??sec:std-Job).
var jobs = []|;
var res = []|;
for (var i : [0, 1, 2])
{
jobs << System.spawn(closure () { res << i; res << i },
true) |
if (i == 2)
break
}|
jobs;
[00009120] [Job<shell_11>, Job<shell_12>, Job<shell_13>]
jobs.each (function (var j) { j.waitForTermination });
assert (res == [0, 1, 0, 2, 1, 2]);
jobs = []|;
res = []|;
for (var i : [0, 1, 2])
{
jobs << System.spawn(closure () { res << i; res << i },
false) |
if (i == 2)
break
}|
jobs;
[00009120] [Job<shell_14>, Job<shell_15>, Job<shell_16>]
sleep(100ms);
assert (res == [0, 1, 0]);
- system(command)
Ask the operating system to run the command. This is typically used to start new processes. The
exact syntax of command depends on your system. On Unix systems, this is typically ‘/bin/sh’,
while Windows uses ‘command.exe’.
Return the exit status.
Windows Issues
Under Windows, the exit status is always 0.
System.system("exit 0"
) == 0;
System.system("exit 23"
) >> 8
== { if (System.Platform.isWindows) 0 else 23 };
- time
Return the number of seconds elapsed since the Urbi server was launched. In presence of a frozen
Tag (??sec:std-Tag), see also shiftedTime.
{ var t0 = time | sleep(1s) | time - t0 }.round ~= 1;
2 ==
{
var t = Tag.new|;
var t0 = time|;
var res;
t: { sleep(1s) | res = time - t0 },
t.freeze;
sleep(1s);
t.unfreeze;
sleep(1s);
res.round;
};
- unsetenv(name)
Undefine the environment variable name, return its previous value. See also getenv and
setenv.
setenv("MyVar"
, 12) == "12"
;
!getenv("MyVar"
).isNil;
unsetenv("MyVar"
) == "12"
;
getenv("MyVar"
).isNil;
20.59 System.PackageInfo
Information about Urbi SDK and its components.
- copyrightHolder
The Urbi SDK copyright holder.
System.PackageInfo.copyrightHolder == "Gostai S.A.S."
;
- copyrightYears
The Urbi SDK copyright years.
System.PackageInfo.copyrightYears == "2005-2010"
;
20.60 System.Platform
A description of the platform (the computer) the server is running on.
- host
The type of system Urbi SDK runs on. Composed of the CPU, the vendor, and the OS.
System.Platform.host ==
"%s-%s-%s"
% [System.Platform.hostCpu,
System.Platform.hostVendor,
System.Platform.hostOs];
- hostCpu
The CPU type of system Urbi SDK runs on. The following values are those for which Gostai
provides binary builds.
System.Platform.hostCpu in ["i386"
, "i686"
, "x86_64"
];
- hostOs
The OS type of system Urbi SDK runs on. For instance darwin9.8.0 or linux-gnu or
mingw32.
- hostVendor
The vendor type of system Urbi SDK runs on. The following values are those for which Gostai
provides binary builds.
System.Platform.hostVendor in ["apple"
, "pc"
, "unknown"
];
- isWindows
Whether running under Windows.
System.Platform.isWindows in [true, false];
- kind
Either "POSIX" or "WIN32".
System.Platform.kind in ["POSIX"
, "WIN32"
];
20.61 Tag
A tag is an object meant to label blocks of code in order to control them externally. Tagged code
can be frozen, resumed, stopped…See also Section 11.3.
20.61.1.1 Stop
To stop a tag means to kill all the code currently running that it labels. It does not affect
“newcomers”.
var t = Tag.new|;
var t0 = time|;
t: every(1s) echo("foo"
),
sleep(2.2s);
[00000158] *** foo
[00001159] *** foo
[00002159] *** foo
t.stop;
sleep(2.2s);
t: every(1s) echo("bar"
),
sleep(2.2s);
[00000158] *** bar
[00001159] *** bar
[00002159] *** bar
t.stop;
System.stop can be used to inject a return value to a tagged expression.
var t = Tag.new|;
var res;
detach(res = { t: every(1s) echo("computing"
) })|;
sleep(2.2s);
[00000001] *** computing
[00000002] *** computing
[00000003] *** computing
t.stop("result"
);
assert(res == "result"
);
Be extremely cautious, the precedence rules can be misleading: var = tag: exp is read as
(var = tag): exp (i.e., defining var as an alias to tag and using it to tag exp), not as
var = { tag: exp }. Contrast the following example, which is most probably an error from the user,
with the previous, correct, one.
var t = Tag.new("t"
)|;
var res;
res = t: every(1s) echo("computing"
),
sleep(2.2s);
[00000001] *** computing
[00000002] *** computing
[00000003] *** computing
t.stop("result"
);
assert(res == "result"
);
[00000004:error] !!! failed assertion: res == "result" (Tag<t> != "result")
To block a tag means:
- Stop running pieces of code it labels (as with stop).
- Ignore new pieces of code it labels (this differs from stop).
One can unblock the tag. Contrary to freeze/unfreeze, tagged code does not resume the
execution.
var ping = Tag.new("ping"
)|;
ping:
every (1s)
echo("ping"
),
assert(!ping.blocked);
sleep(2.1s);
[00000000] *** ping
[00002000] *** ping
[00002000] *** ping
ping.block;
assert(ping.blocked);
ping:
every (1s)
echo("pong"
),
ping.unblock;
assert(!ping.blocked);
sleep(2.1s);
ping:
every (1s)
echo("ping again"
),
sleep(2.1s);
[00004000] *** ping again
[00005000] *** ping again
[00006000] *** ping again
As with stop, one can force the value of stopped expressions.
["foo"
, "foo"
, "foo"
]
==
{
var t = Tag.new;
var res = [];
for (3)
detach(res << {t: sleep(inf)});
t.block("foo"
);
res;
};
To freeze a tag means holding the execution of code it labels. This applies to code already being run,
and “arriving” pieces of code.
var t = Tag.new|;
var t0 = time|;
t: every(1s) echo("time : %.0f"
% (time - t0)),
sleep(2.2s);
[00000158] *** time : 0
[00001159] *** time : 1
[00002159] *** time : 2
t.freeze;
assert(t.frozen);
t: every(1s) echo("shifted: %.0f"
% (shiftedTime - t0)),
sleep(2.2s);
t.unfreeze;
assert(!t.frozen);
sleep(2.2s);
[00004559] *** shifted: 2
[00005361] *** time : 5
[00005560] *** shifted: 3
[00006362] *** time : 6
[00006562] *** shifted: 4
Scopes feature a scopeTag, i.e., a tag which will be stop when the execution reaches the end of
the current scope. This is handy to implement cleanups, how ever the scope was exited
from.
{
var t = scopeTag;
t: every(1s)
echo("foo"
),
sleep(2.2s);
};
[00006562] *** foo
[00006562] *** foo
[00006562] *** foo
{
var t = scopeTag;
t: every(1s)
echo("bar"
),
sleep(2.2s);
throw 42;
};
[00006562] *** bar
[00006562] *** bar
[00006562] *** bar
[00006562:error] !!! 42
sleep(2s);
Tags provide two events, enter and leave, that trigger whenever flow control enters or leaves
statements the tag.
var t = Tag.new("t"
);
[00000000] Tag<t>
at (t.enter?)
echo("enter"
);
at (t.leave?)
echo("leave"
);
t: {echo("inside"
); 42};
[00000000] *** enter
[00000000] *** inside
[00000000] *** leave
[00000000] 42
This feature is fundamental; it is a concise and safe way to ensure code will be executed upon
exiting a chunk of code (like raii in C ++ or finally in Java). The exit code will be run no matter
what the reason for leaving the block was: natural exit, exceptions, flow control statements like return
or break, …
For instance, suppose we want to make sure we turn the gas off when we’re done cooking. Here is
the bad way to do it:
{
function cook()
{
turn_gas_on();
turn_gas_off();
}|
enter_the_kitchen();
cook();
leave_the_kitchen();
};
This is wrong because there are several situations where we could leave the kitchen with gas still
turned on. Consider the following cooking code:
{
function cook()
{
turn_gas_on();
if (meal_ready)
{
echo("The meal is already there, nothing to do!"
);
return
};
for (var ingredient in recipe)
if (ingredient not in kitchen)
throw Exception("missing ingredient: %s"
% ingredient)
else
put_ingredient();
turn_gas_off();
}|
enter_the_kitchen();
cook();
leave_the_kitchen();
};
Here, if the meal was already prepared, or if an ingredient is missing, we will leave the cook
function without executing the turn_gas_off statement, through the return statement or the
exception. The right way to ensure gas is necessarily turned off is:
{
function cook()
{
var with_gas = Tag.new("with_gas"
);
at (with_gas.enter?)
turn_gas_on();
at (with_gas.leave?)
turn_gas_off();
with_gas: {
}
}|
enter_the_kitchen();
cook();
leave_the_kitchen();
};
The begin and end methods enable to monitor when code is executed. The following example
illustrates the proper use of enter and leave events (Section 20.61.1.5), which are used to implement
this feature.
var mytag = Tag.new("mytag"
);
[00000000] Tag<mytag>
mytag.begin: echo(1);
[00000000] *** mytag: begin
[00000000] *** 1
mytag.end: echo(2);
[00000000] *** 2
[00000000] *** mytag: end
mytag.begin.end: echo(3);
[00000000] *** mytag: begin
[00000000] *** 3
[00000000] *** mytag: end
As any object, tags are created using new to create derivatives of the Tag object. The name is optional,
it makes easier to display a tag and remember what it is.
var t1 = Tag.new;
[00000001] Tag<tag_8>
var t2 = Tag.new("cool name"
);
[00000001] Tag<cool name>
- begin
A sub-tag that prints out ”tag_name: begin” each time flow control enters the tagged code.
See Section 20.61.1.6.
- block(result = void)
Block any code tagged by this. Blocked tags can be unblocked using unblock. If some
result was specified, let stopped code return result as value. See Section 20.61.1.2.
- blocked
Whether code tagged by this is blocked. See Section 20.61.1.2.
- end
A sub-tag that prints out ”tag_name: end” each time flow control leaves the tagged code.
See Section 20.61.1.6.
- enter
An event triggered each time the flow control enters the tagged code. See Section 20.61.1.5.
- freeze
Suspend code tagged by this, already running or forthcoming. Frozen code can be later
unfrozen using unfreeze. See Section 20.61.1.3.
- frozen
Whether the tag is frozen. See Section 20.61.1.3.
- leave
An event triggered each time flow control leaves the tagged code. See Section 20.61.1.5.
- scope
Return a fresh Tag whose stop will be invoked a the end of the current scope. This function
is likely to be removed. See Section 20.61.1.4.
- stop(result = void)
Stop any code tagged by this. If some result was specified, let stopped code return
result as value. See Section 20.61.1.1.
- tags
All the undeclared tags are created as slots in this object. Using this feature is discouraged.
{
assert ("brandNewTag"
not in Tag.tags.localSlotNames);
brandNewTag: 1;
assert ("brandNewTag"
in Tag.tags.localSlotNames);
assert (Tag.tags.brandNewTag.isA(Tag));
};
- unblock
Unblock this. See Section 20.61.1.2.
- unfreeze
Unfreeze code tagged by this. See Section 20.61.1.3.
Tags can be arranged in a parent/child relationship: any operation done on a tag — freezing, stopping,
…is also performed on its descendants. Another way to see it is that tagging a piece of
code with a child will also tag it with the parent. To create a child Tag, simply clone its
parent.
var parent = Tag.new |
var child = parent.clone |
{
parent: {sleep(100ms); echo("parent"
)},
child: {sleep(100ms); echo("child"
)},
parent.stop;
sleep(200ms);
echo("end"
);
};
[00000001] *** end
{
parent: {sleep(100ms); echo("parent"
)},
child: {sleep(100ms); echo("child"
)},
child.stop;
sleep(200ms);
echo("end"
);
};
[00000002] *** parent
[00000003] *** end
Hierarchical tags are commonly laid out in slots so as to reflect their tag hierarchy.
var a = Tag.new;
var a.b = a.clone;
var a.b.c = a.b.clone;
a: foo;
a.b: bar;
a.b.c: baz;
20.62 Timeout
Timeout objects can be used as Tags (??sec:std-Tag) to execute some code in limited
time.
At construction, a Timeout takes a duration, and a Boolean (??sec:std-Boolean) stating whether an
exception should be thrown on timeout (by default, it does).
Timeout.new(300ms);
[00000000] Timeout_0x953c1e0
Timeout.new(300ms, false);
[00000000] Timeout_0x953c1e0
Use it as a tag:
var t = Timeout.new(300ms);
[00000000] Timeout_0x133ec0
t:{
echo("This will be displayed."
);
sleep(500ms);
echo("This will not."
);
};
[00000000] *** This will be displayed.
[00000007:error] !!! Timeout_0x133ec0 has timed out.
The same Timeout, t can be reused. It is armed again each time it is used to tag some
code.
t: { echo("Open"
); sleep(1s); echo("Close"
); };
[00000007] *** Open
[00000007:error] !!! Timeout_0x133ec0 has timed out.
t: { echo("Open"
); sleep(1s); echo("Close"
); };
[00000007] *** Open
[00000007:error] !!! Timeout_0x133ec0 has timed out.
Even if exceptions have been disabled, you can check whether the count-down expired with
timedOut.
t:sleep(500ms);
[00000007:error] !!! Timeout_0x133ec0 has timed out.
if (t.timedOut)
echo("The Timeout expired."
);
[00000000] *** The Timeout expired.
20.63 TrajectoryGenerator
The trajectory generators change the value of a given variable from an initial value to a target
value. They can be open-loop, i.e., the intermediate values depend only on the initial and/or target
value of the variable; or closed-loop, i.e., the intermediate values also depend on the current value value
of the variable.
Open-loop trajectories are insensitive to changes made elsewhere to the variable. Closed-loop
trajectories are sensitive to changes made elsewhere to the variable — for instance when the human
physically changes the position of a robot’s motor.
Trajectory generators are not made to be used directly, rather use the “continuous assignment”
syntax (Section 19.10).
The Accel trajectory reaches a target value at a fixed acceleration (accel attribute).
20.63.2.2 Cos
The Cos trajectory implements a cosine around the target value, given an amplitude (ampli attribute)
and period (cos attribute).
This trajectory is not “smooth”: the initial value of the variable is not taken into account.
20.63.2.3 Sin
The Sin trajectory implements a sine around the target value, given an amplitude (ampli attribute)
and period (sin attribute).
This trajectory is not “smooth”: the initial value of the variable is not taken into account.
The Smooth trajectory implements a sigmoid. It changes the variable from its current value to the
target value “smoothly” in a given amount of time (smooth attribute).
The Speed trajectory changes the value of the variable from its current value to the target value at a
fixed speed (the speed attribute).
If the adaptive attribute is set to true, then the duration of the trajectory is constantly
reevaluated.
20.63.2.6 Time
The Time trajectory changes the value of the variable from its current value to the target value within
a given duration (the time attribute).
If the adaptive attribute is set to true, then the duration of the trajectory is constantly
reevaluated.
Trajectories can be managed using Tags (??sec:std-Tag). Stopping or blocking a tag that manages a
trajectory kill the trajectory.
When a trajectory is frozen, its local time is frozen too, the movement proceeds from where it was
rather than from where it would have been had it been not frozen.
You are not expected to construct trajectory generators by hand, using modifiers is the recommended
way to construct trajectories. See Section 19.10 for details about trajectories, and see Section 20.63.2
for an extensive set of examples.
- Accel
This class implements the Accel trajectory (Section 20.63.2.1). It derives from OpenLoop.
- ClosedLoop
This class factors the implementation of the closed-loop trajectories. It derives from
TrajectoryGenerator.
- OpenLoop
This class factors the implementation of the open-loop trajectories. It derives from
TrajectoryGenerator.
- Sin
This class implements the Cos and Sin trajectories (Section 20.63.2.2, Section 20.63.2.3).
It derives from OpenLoop.
- Smooth
This class implements the Smooth trajectory (Section 20.63.2.4). It derives from OpenLoop.
- SpeedAdaptive
This class implements the Speed trajectory when the adaptive attribute is given
(Section 20.63.2.5). It derives from ClosedLoop.
- Time
This class implements the non-adaptive Speed and Time trajectories (Section 20.63.2.5,
Section 20.63.2.6). It derives from OpenLoop.
- TimeAdaptive
This class implements the Time trajectory when the adaptive attribute is given
(Section 20.63.2.6). It derives from ClosedLoop.
20.64 Triplet
A triplet (or triple) is a container storing three objects.
A Triplet is constructed with three arguments.
Triplet.new(1, 2, 3);
[00000001] (1, 2, 3)
Triplet.new(1, 2);
[00000003:error] !!! Triplet.init: expected 3 arguments, given 2
Triplet.new(1, 2, 3, 4);
[00000003:error] !!! Triplet.init: expected 3 arguments, given 4
- first
Return the first member of the pair.
Triplet.new(1, 2, 3).first == 1;
Triplet[0] === Triplet.first;
- second
Return the second member of the triplet.
Triplet.new(1, 2, 3).second == 2;
Triplet[1] === Triplet.second;
- third
Return the third member of the triplet.
Triplet.new(1, 2, 3).third == 3;
Triplet[2] === Triplet.third;
20.65 Tuple
A tuple is a container storing a fixed number of objects. Examples include Pair (??sec:std-Pair)
and Triplet (??sec:std-Triplet).
The Tuple object is not meant to be instantiated, its main purpose is to share code for its descendants,
such as Pair (??sec:std-Pair). Yet it accepts its members as a list.
var t = Tuple.new([1, 2, 3]);
[00000000] (1, 2, 3)
The output generated for a Tuple can also be used to create a Tuple. Expressions are put inside
parentheses and separated by commas. One extra comma is allowed after the last element. To avoid
confusion between a 1 member Tuple and a parenthesized expression, the extra comma must be added.
Tuple with no expressions are also accepted.
(1);
[00000000] 1
();
[00000000] ()
(1,);
[00000000] (1,)
(1, 2);
[00000000] (1, 2)
(1, 2, 3, 4,);
[00000000] (1, 2, 3, 4)
- asString
The string ‘(first, second, ..., last)’, using asPrintable to convert members to
strings.
().asString == "()"
;
(1,).asString == "(1,)"
;
(1, 2).asString == "(1, 2)"
;
(1, 2, 3, 4,).asString == "(1, 2, 3, 4)"
;
- matchAgainst(handler, pattern)
Pattern matching on members. See Pattern (??sec:std-Pattern).
{
(var first, var second) = (1, 2);
assert { first == 1; second == 2 };
};
- size
Number of members.
().size == 0;
(1,).size == 1;
(1, 2, 3, 4).size == 4;
(1, 2, 3, 4,).size == 4;
- ’[]’(index)
Return the index-th element. index must be in bounds.
(1, 2, 3)[0] == 1;
(1, 2, 3)[1] == 2;
- ’[]=’(index, value)
Set (and return) the index-th element to value. index must be in bounds.
{
var t = (1, 2, 3);
assert
{
(t[0] = 2) == 2;
t == (2, 2, 3);
};
};
- ’<’(other)
—
Lexicographic comparison between two tuples.
(0, 0) < (0, 1);
(0, 0) < (1, 0);
(0, 1) < (1, 0);
- ’==’(other)
—
Whether this and other have the same contents (equality-wise).
(1, 2) == (1, 2);
!((1, 1) == (2, 2));
- ’*’(value)
—
Return a Tuple in which all elements of this are multiplied by a value.
(0, 1, 2, 3) * 3 == (0, 3, 6, 9);
(1, "foo"
) * 2 == (2, "foofoo"
);
- ’+’(other)
—
Return a Tuple in which each element of this is summed with its corresponding element in the
other Tuple.
(0, 1) + (2, 3) == (2, 4);
(1, "foo"
) + (2, "bar"
) == (3, "foobar"
);
20.66 UObject
UObject is used by the UObject API (see Chapter I) to represent a bound C ++ instance.
All the UObjects are copied under an unique name as slots of uobjects.
- __uobjectName
Unique name assigned to this object. This is also the slot name of Global.uobjects
containing this UObject.
20.67 UValue
The UValue object is used internally by the UObject API and is mostly hidden from the
user.
20.68 UVar
This class is used internally by the UObject middleware (see Chapter I) to represent a variable
that can be hooked in C++. Each slot on which a C++ urbi::UVar exists contains an instance of this
class.
Instances of UVar are mostly transparent, they appear as the value they contain. Thus, since
the UVar evaluates to the contained value, you must use getSlot to manipulate the UVar
itself.
To instantiate a new UVar, pass the owner object and the slot name to the constructor.
UVar.new(Global, "x"
)|
Global.x = 5;
[000000001] 5
x;
[000000002] 5
- copy(targetObject, targetName)
Copy the UVar to the slot targetName of object targetObject. Since the UVar is using
properties, you must use this method to copy it to an other location.
- notifyChange(handle, onChange)
Similar to the C++ UNotifyChange (see Section 3.5) , register onChange and call it each time
this UVar is written to. handle is a WeakPointer used to automatically unregister the callback.
You can use the global uobjects_handle.
UVar.new(Global, "y"
)|
Global.getSlot("y"
).notifyChange(uobjects_handle, closure() {
echo("The value is now "
+ Global.y)
})|
Global.y = 12;
[00000001] *** The value is now 12
[00000002] 12
- notifyAccess(handle, onAccess)
Similar to the C++ UNotifyAccess, calls onAccess each time the UVar is accessed
(read).
- owned
True if the UVar is in owned mode, that is if it contains both a sensor and a command
value.
20.69 void
The special entity void is an object used to denote “no value”. It has no prototype and cannot be
used as a value. In contrast with nil (??sec:std-nil), which is a valid object, void denotes a value one
is not allowed to read.
None.
void is the value returned by constructs that return no value.
void.isVoid;
{}.isVoid;
{if (false) 123}.isVoid;
- acceptVoid
Trick this so that, even if it is void it can be used as a value. See also unacceptVoid.
void.foo;
[00096374:error] !!! unexpected void
void.acceptVoid.foo;
[00102358:error] !!! lookup failed: foo
- isVoid
Whether this is void. Therefore, return true.
void.isVoid;
void.acceptVoid.isVoid;
! 123.isVoid;
- unacceptVoid
Remove the magic from this that allowed to manipulate it as a value, even if it void. See also
acceptVoid.
void.acceptVoid.unacceptVoid.foo;
[00096374:error] !!! unexpected void