This chapter details the predefined objects.
21.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. Calls to signal and wait 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.
Return the number of jobs 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
21.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.
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.
21.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.
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:
- false, nil
false.
- raise an error.
- false iff null (Float).
- Dictionary, List, String
false iff empty (Dictionary, List, String).
- otherwise
true.
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
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;
- ’!’
Logical negation. If this is false return true and vice-versa.
!true === false;
!false === true;
- asBool
Identity.
true.asBool === true;
false.asBool === false;
21.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)
} |;
// Call twice echo("foo").
callTwice(1, echo("foo"), echo("bar"));
[00000001] *** foo
[00000002] *** foo
// Call twice echo("bar").
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 not yet evaluated arguments.
function args { call.args }|
assert
{
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({echo(1); 1}) == 1;
argsCount({echo(1); 1}, {echo(2); 2}) == 2;
};
- code
The body of the called function as a Code.
function code { call.getSlotValue("code") }|
assert (code == getSlotValue("code"));
- eval
Evaluate this, and return the result.
var c1 = do (CallMessage.new())
{
var this.target = 1;
var this.message = "+";
var this.args = [Lazy.new(function () {2})];
}|
assert { c1.eval() == 3 };
// A lazy function that returns the sum of this and the second argument,
// regardless of the first argument.
function Float.addSecond
{
this + call.evalArgAt(1);
}|
var c2 = do (CallMessage.new())
{
var this.target = 2;
var this.message = "addSecond";
var this.args = [Lazy.new(function (){ assert (false) }),
Lazy.new(function (){ echo (5); 5 })];
}|
assert { c2.eval() == 7 };
[00000454] *** 5
- 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 21.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] !!! sumTwice: invalid index: 3
[01234567:error] !!! called from: evalArgAt
[00000014:error] !!! called from: sumTwice
sumTwice(3.14, one(), one());
[00000015:error] !!! sumTwice: invalid index: 3.14
[01234567:error] !!! called from: evalArgAt
[00000015:error] !!! called from: sumTwice
- 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 Object.getSender { call.sender } |
function Object.callGetSender { getSender() } |
assert
{
// Call from the current Lobby, with the Lobby as target.
getSender() === lobby;
// Call from the current Lobby, with Object as the target.
Object.getSender() === lobby;
// Ask Lobby to call getSender.
callGetSender() === lobby;
// Ask Global to call getSender.
Object.callGetSender() === Object;
};
- 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 Object.getTarget { call.target } |
function Object.callGetTarget { getTarget() } |
assert
{
// Call from the current Lobby, with the Lobby as target.
getTarget() === lobby;
// Call from the current Lobby, with Global as the target.
Object.getTarget() === Object;
// Ask Lobby to call getTarget.
callGetTarget() === lobby;
// Ask Global to call getTarget.
Object.callGetTarget() === Object;
};
21.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<my_label>
ch1 << 1;
[00000201:my_label] 1
var ch2 = ch1;
[00000201] Channel<my_label>
ch2 << 1/2;
[00000201:my_label] 0.5
21.6 Code
Functions written in urbiscript.
The keywords function and closure build Code instances.
function(){}.protos[0] === ’package’.lang.getSlotValue("Code");
closure (){}.protos[0] === ’package’.lang.getSlotValue("Code");
- ’==’(that)
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 Object.capture_x() { x };
function Object.capture_x_again () { x };
{
var x;
function Object.capture_another_x() { x };
}
}|;
assert
{
getSlotValue("capture_x") == getSlotValue("capture_x_again");
getSlotValue("capture_x") != getSlotValue("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;
// There is Object.apply.
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.
closure () { 1 }.asString() == "closure () { 1 }";
function () { 1 }.asString() == "function () { 1 }";
- bodyString
Conversion to String of the routine body.
closure () { 1 }.bodyString() == "1";
function () { 1 }.bodyString() == "1";
- spawn(clear)
Run this, with fresh tags if clear is true, otherwise under the control of the current tags.
Return the spawn Job. This is an internal function, instead, use detach and disown.
var jobs = []|;
var res = []|;
for (var i : [0, 1, 2])
{
jobs << closure () { res << i; res << i }.spawn(true) |
if (i == 2)
break
}|
jobs;
[00009120] [Job<shell_7>, Job<shell_8>, Job<shell_9>]
// Wait for the jobs to be done.
jobs.each (function (var j) { j.waitForTermination });
assert (res == [0, 1, 0, 2, 1, 2]);
jobs = []|;
res = []|;
for (var i : [0, 1, 2])
{
jobs << closure () { res << i; res << i }.spawn(false) |
if (i == 2)
break
}|
jobs;
[00009120] [Job<shell_10>, Job<shell_11>, Job<shell_12>]
// Give some time to get the output of the detached expressions.
sleep(100ms);
assert (res == [0, 1, 0]);
21.7 Comparable
Objects that can be compared for equality and inequality. See also 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 ’==’ (that) { value == that.value; };
}|;
assert
{
Foo.new(1) == Foo.new(1);
Foo.new(1) != Foo.new(2);
};
- ’!=’(that)
Whether ! (this == that).
class FiftyOne : Comparable
{
function ’==’ (that) { 51 == that };
}|;
assert
{
FiftyOne == 51;
FiftyOne != 42;
};
- ’==’(that)
Whether ! (this != that).
class FortyTwo : Comparable
{
function ’!=’ (that) { 42 != that };
}|;
assert
{
FortyTwo != 51;
FortyTwo == 42;
};
21.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;
};
21.9 Control
Control is a namespace for control sequences used by the Urbi engine to execute some urbiscript
features. It is internal; in other words, users are not expected to use it, much less change
it.
21.10 Date
This class is meant to record dates in time, with microsecond resolution. See also System.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 current date.
Date.new;
[00000001] 2010-08-17 14:40:52.549726
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:637");
[00000001] 2003-10-10 20:10:50.637000
Date.new("2003-10-10 20:10:50");
[00000001] 2003-10-10 20:10:50.000000
Date.new("2003-Oct-10 20:10");
[00000002] 2003-10-10 20:10:00.000000
Date.new("2003-10-10 20");
[00000003] 2003-10-10 20:00:00.000000
Pay attention that the format is rather strict; for instance too many spaces between day and time
result in an error.
Date.new("2003-10-10 20:10:50");
[00001968:error] !!! new: cannot convert to date: 2003-10-10 20:10:50
Pay attention that the format is not strict enough either; for instance, below, the ‘.’ separator seem
to prefix microseconds, but actually merely denotes the minutes. Seconds must be spelled out in order
to introduce microseconds.
Date.new("2003-10-10 00.12");
[00000003] 2003-10-10 00:12:00.000000
Date.new("2003-10-10 00:00.12");
[00000003] 2003-10-10 00:00:12.000000
21.11 Dictionary
A dictionary is an associative array, also known as a hash in some programming languages. They
are arrays whose indexes are arbitrary objects.
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
Arbitrary objects can be used as dictionary keys. To map to the same cell, two objects used as keys
must have equal hashes (retrieved with the Object.hash method) and be equal to each other (in the
Object.’==’ sense).
This means that two different objects may have the same hash: the equality operator
(Object.’==’) is checked in addition to the hash, to handle such collision. However a good hash
algorithm should avoid this case, since it hinders performances.
See Object.hash for more detail on how to override hash values. Most standard value-based
classes implement a reasonable hash function: see Float.hash, String.hash, List.hash,
…
The Dictionary constructor takes arguments by pair (key, value).
Dictionary.new("one", 1, "two", 2);
[00000000] ["one" => 1, "two" => 2]
Dictionary.new();
[00000000] [ => ]
There must be an even number of arguments.
Dictionary.new("1", 2, "3");
[00000001:error] !!! new: odd number of arguments
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]
It is guaranteed that the pairs to insert are evaluated left-to-write, key first, the value.
["a".fresh() => "b".fresh(), "c".fresh() => "d".fresh()]
== ["a_5" => "b_6", "c_7" => "d_8"];
Duplicate keys in Dictionary literal are an error. On this regards, urbiscript departs from choices
made in JavaScript, Perl, Python, Ruby, and probably many other languages.
["one" => 1, "one" => 2];
[00000001:error] !!! duplicate dictionary key: "one"
- ’==’(that)
Whether this equals that. Expects members to be Comparable.
[ => ] == [ => ];
["a" => 1, "b" => 2] == ["b" => 2, "a" => 1];
- ’[]’(key)
Syntactic sugar for get(key).
assert (["one" => 1]["one"] == 1);
["one" => 1]["two"];
[00000012:error] !!! missing key: two
- ’[]=’(key, value)
Syntactic sugar for set(key, value), but returns value.
var d = ["one" =>"2"];
(d["one"] = 1) == 1;
d["one"] == 1;
- asBool
Negation of empty.
[=>].asBool() == false;
["key" => "value"].asBool() == true;
- asList
The contents of the dictionary as a Pair list (key, value).
["one" => 1, "two" => 2].asList() == [("one", 1), ("two", 2)];
Since Dictionary derives from RangeIterable, it is easy to iterate over a Dictionary using a
range-for (Listing 20.7.6). 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]";
- asTree
Display the content of the List as a tree representation.
echo("simple dictionary:" + ["key1" => "elt1", "key2" => ["key3" => "elt3"]].asTree());
[00000001] *** simple dictionary:
[
key1 => elt1,
key2 =>
[
key3 => elt3,
]
]
echo("dictionary with list:" +
["key1" => "elt1", "key2" => ["key3" => ["key4", "key5"]]].asTree());
[00000002] *** dictionary with list:
[
key1 => elt1,
key2 =>
[
key3 =>
[
key4,
key5,
]
]
]
- clear
Empty the dictionary.
["one" => 1].clear().empty;
- elementAdded
An event emitted each time a new element is added to the Dictionary.
- elementChanged
An event emitted each time the value associated to a key of the Dictionary is changed.
- elementRemoved
An event emitted each time an element is removed from the Dictionary.
d = [ => ] |;
at(d.elementAdded?) echo ("added");
at(d.elementChanged?) echo ("changed");
at(d.elementRemoved?) echo ("removed");
d["key1"] = "value1";
[00000001] "value1"
[00000001] *** added
d["key2"] = "value2";
[00000001] "value2"
[00000001] *** added
d["key2"] = "value3";
[00000001] "value3"
[00000001] *** changed
d.erase("key2");
[00000002] ["key1" => "value1"]
[00000001] *** removed
d.clear();
[00000003] [ => ]
[00000001] *** removed
d.clear();
[00000003] [ => ]
- empty
Whether the dictionary is empty.
[=>].empty == true;
["key" => "value"].empty == false;
- erase(key)
Remove the mapping for key.
{
var d = ["one" => 1, "two" => 2];
assert
{
d.erase("two") === d;
d == ["one" => 1];
};
try
{
["one" => 1, "two" => 2].erase("three");
echo("never reached");
}
catch (var e if e.isA(Dictionary.KeyError))
{
assert(e.key == "three")
};
};
- get(key)
The value associated to key. A Dictionary.KeyError exception is thrown if the key is missing.
var d = ["one" => 1, "two" => 2]|;
assert(d.get("one") == 1);
["one" => 1, "two" => 2].get("three");
[00000010:error] !!! missing key: three
try
{
d.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.
var d = ["one" => 1, "two" => 2];
d.getWithDefault("one", -1) == 1;
d.getWithDefault("three", 3) == 3;
- has(key)
Whether the dictionary has a mapping for key.
var d = ["one" => 1];
d.has("one");
!d.has("zero");
The infix operators in and not in use has (see Section 20.1.8.7).
"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 features the same function,
uniform iteration over a List or a Dictionary is possible.
var d = ["one" => 1, "two" => 2];
d.keys == ["one", "two"];
- matchAgainst(handler, pattern)
Pattern matching on members. See Pattern.
{
// Match a subset of the dictionary.
["a" => var a] = ["a" => 1, "b" => 2];
// get the matched value.
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("two", 2)
.set("one", 1);
[00000000] ["one" => 1, "two" => 2]
- size
Number of element in the dictionary.
var d = [=>]; d.size == 0;
d["a"] = 10; d.size == 1;
d["b"] = 20; d.size == 2;
d["a"] = 30; d.size == 2;
21.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 or a
Path. It can also be constructed by the method open of Path.
Directory.new(".");
[00000001] Directory(".")
Directory.new(Path.new("."));
[00000002] Directory(".")
- ’/’(str)
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
The str String is concatenated with the directory path. If the resulting path is
either a directory or a file, ’/’ will returns either a Directory or a File object.
var dir1 = Directory.create("dir1")|;
var dir2 = Directory.create("dir1/dir2")|;
var file = File.create("dir1/file")|;
dir1 / "dir2";
[00000001] Directory("dir1/dir2")
dir1 / "file";
[00000002] File("dir1/file")
- ’<<’(entity)
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
If entity is a Directory or a File, ’<<’ copies entity in the this directory. Return this to
allow chained operations.
var dir1 = Directory.create("dir1")|;
var dir2 = Directory.create("dir2")|;
var file = File.create("file")|;
dir1 << file << dir2;
[00000001] Directory("dir1")
dir1.content;
[00000003] ["dir2", "file"]
dir2;
[00000004] Directory("dir2")
file;
[00000005] File("file")
- asList
The contents of the directory as a Path list. The various paths include the name of the directory
this.
- asPath
A Path being the path of the directory.
- asString
A String containing the path of the directory.
Directory.new(".").asString() == ".";
- basename
Return a String containing the path of the directory without its dirname. Deprecated, use
asPath.basename instead.
var dir1 = Directory.create("dir1");
var dir2 = Directory.create("dir1/dir2");
dir1.basename == dir1.asPath().basename == "dir1";
[00000808:warning] !!! ‘basename’ is deprecated, use ‘asPath.basename’
dir2.basename == dir2.asPath().basename == "dir2";
[00000811:warning] !!! ‘basename’ is deprecated, use ‘asPath.basename’
- clear
Remove all children recursively but not the directory itself. After a call to clear, a call to empty
should return true.
var dir1 = Directory.create("dir1");
var dir2 = Directory.create("dir1/dir2");
var file1 = File.create("dir1/file1");
var file2 = File.create("dir1/dir2/file2");
dir1.content == ["dir2", "file1"];
dir2.content == ["file2"];
dir1.clear().isVoid;
dir1.empty;
- content
The contents of the directory as a String list. The strings include only the last component name;
they do not contain the directory name of this.
- copy(dirname)
Copy recursively all items of the this directory into the directory dirname after creating it.
var dir1 = Directory.create("dir1")|;
var dir2 = Directory.create("dir1/dir2")|;
var file = File.create("dir1/file")|;
var directory1 = dir1.copy("directory1")|;
dir1;
[00000002] Directory("dir1")
directory1.content;
[00000003] ["dir2", "file"]
- copyInto(dirname)
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
Copy this into dirname without creating it.
var dir = Directory.create("dir")|;
var dir1 = Directory.create("dir1")|;
var dir2 = Directory.create("dir1/dir2")|;
var file = File.create("dir1/file")|;
dir1.copyInto(dir);
[00000001] Directory("dir/dir1")
dir1;
[00000002] Directory("dir1")
dir1.content;
[00000003] ["dir2", "file"]
dir.content;
[00000004] ["dir1"]
Directory.new("dir/dir1").content;
[00000005] ["dir2", "file"]
- create(name)
Create the directory name where name is either a String or a Path. In addition to
system errors that can occur, errors are raised if directory or file name already exists.
Directory.new("dir");
[00000001:error] !!! new: no such file or directory: dir
var dir = Directory.create("dir");
[00000002] Directory("dir")
dir = Directory.create("dir");
[00000001:error] !!! create: directory already exists: dir
dir.content;
[00000003] []
- createAll(name)
Create the directory name where name is either a String or a Path. If name is a path (or
a String describing a path) no errors are raised if one directory doesn’t exist or
already exists. Instead createAll creates them all as in the Unix ‘make -p’ command.
Directory.create("dir1/dir2/dir3");
[00000001:error] !!! create: no such file or directory: "dir1/dir2/dir3"
var dir1 = Directory.create("dir1");
[00000002] Directory("dir1")
Directory.createAll("dir1/dir2/dir3");
[00000002] Directory("dir1/dir2/dir3")
- empty
Whether the directory is empty.
var dir = Directory.create("dir");
dir.empty;
File.create("dir/file");
!dir.empty;
- exists
Whether the directory still exists.
var dir = Directory.create("dir");
dir.exists;
dir.remove().isVoid;
!dir.exists;
- 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();
}|;
- lastModifiedDate
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
Return a Date object stating when the directory was last modified. Deprecated, use
asPath.lastModifiedDate instead.
- moveInto(dirname)
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
Move this into dirname without creating it.
var dir1 = Directory.create("dir1")|;
var dir2 = Directory.create("dir1/dir2")|;
var file = File.create("dir1/file")|;
var dir = Directory.create("dir")|;
dir1.moveInto(dir);
[00000001:warning] !!! ‘rename(name)’ is deprecated, use ‘asPath.rename(name)’
dir1;
[00000002] Directory("dir/dir1")
assert
{
dir1.content == ["dir2", "file"];
dir.content == ["dir1"]
};
- parent
The Directory parent of this.
Directory.create("dir")|;
var dir = Directory.create("dir/dir")|;
dir.parent;
[00000001] Directory("dir")
assert(dir.parent.parent.asString() == Directory.current.asString());
- remove
Remove this directory if it is empty.
var dir = Directory.create("dir")|;
File.create("dir/file")|;
dir.remove();
[00000001:error] !!! remove: directory not empty: dir
dir.clear();
dir.remove();
assert(!dir.exists);
- removeAll
Remove all children recursively including the directory itself.
var dir1 = Directory.create("dir1")|;
var dir2 = Directory.create("dir1/dir2")|;
var file1 = File.create("dir1/file1")|;
var file2 = File.create("dir1/dir2/file2")|;
dir1.removeAll();
assert(!dir1.exists);
- rename
Rename or move the directory. Deprecated, use asPath.rename instead.
var dir = Directory.create("dir")|;
File.create("dir/file")|;
dir.rename("other");
[00000001:warning] !!! ‘rename(name)’ is deprecated, use ‘asPath.rename(name)’
dir;
[00000002] Directory("other")
dir.content;
[00000003] ["file"]
var dir2 = Directory.create("dir2")|;
dir.rename("dir2/other2");
[00000002:warning] !!! ‘rename(name)’ is deprecated, use ‘asPath.rename(name)’
dir;
[00000005] Directory("dir2/other2")
dir.content;
[00000006] ["file"]
- size
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
The size of all the directory content computed recursively, in number of bytes.
var dir = Directory.create("dir")|;
Directory.create("dir/dir")|;
File.save("dir/file", "content");
var file1 = File.create("dir/file")|;
File.save("dir/dir/file", "content");
var file2 = File.create("dir/dir/file")|;
assert(dir.size() == file1.size() + file2.size());
21.13 Duration
This class records differences between Dates.
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)
- asFloat
Return the duration as a Float.
Duration.new(1000).asFloat() == 1000;
Duration.new(1000.1234).asFloat() == 1000.1234;
- asString
Return the duration as a String.
Duration.new(1000).asString() == "1000s";
- seconds
Return the duration as a Float.
Duration.new(1000) .seconds() == 1000;
Duration.new(1000.52).seconds() == 1000.52;
21.14 Enumeration
Prototype of enumeration types.
See Section 20.5.
An Enumeration is created with two arguments: the name of the enumeration type, and the list of
possible values. Most of the time, it is a good idea to store it in a variable with the same
name.
var Direction = Enumeration.new("Direction", ["up", "down", "left", "right"]);
[00000001] Direction
Direction.up;
[00000002] up
The following syntax is equivalent.
enum Direction
{
up,
down,
left,
right
};
[00000001] Direction
The created values are derive from the created enumeration type.
Direction.isA(Enumeration);
Direction.up.isA(Direction);
21.15 Event
An event can be “emitted” and “caught”, or “sent” and “received”. See also Section 11.2.
There are several examples of uses of events in the documentation of event-based constructs.
See at (Listing 20.11.1), waituntil (Listing 20.11.2), whenever (Listing 20.11.4), and so
forth. The tutorial chapter about event-based programming contains other examples, see
Listing 11.
A particular emphasis should be put on the synchronicity of the event handling, i.e., whether the
bodies of the event handlers are run before the control flow returns from the event emission. By
default, (i.e., at (e?...) and e!(...)/e.emit(...))) the execution is asynchronous, but if either the
emitted or the handler is marked asynchronous (i.e., at sync (e?...) or e.syncEmit(...)), then the
execution is synchronous.
Contrast the following examples:
Asynchronous handlers
var e = Event.new() |; at (e?) { echo("a"); sleep(20ms); echo("b") } onleave { echo("c"); sleep(20ms); echo("d") }; e! | echo("done"); [00000001] *** done sleep(25ms); [00000002] *** a [00000003] *** c [00000101] *** b [00000102] *** d e.syncEmit() | echo("done"); [00000001] *** a [00000101] *** b [00000102] *** c [00000202] *** d [00000203] *** done
|
Synchronous handlers
var e = Event.new() |; at sync (e?) { echo("a"); sleep(20ms); echo("b") } onleave { echo("c"); sleep(20ms); echo("d") }; e! | echo("done"); // No need to sleep. [00000011] *** a [00000031] *** b [00000031] *** c [00000052] *** d [00000052] *** done e.syncEmit() | echo("done"); [00000052] *** a [00000073] *** b [00000073] *** c [00000094] *** d [00000094] *** done
|
For more information about the synchronicity of event handlers, see Listing 20.11.1.3.
Events can also be sustained during a time span starting at trigger and ending at handler.stop.
Note that the onleave-clauses of the event handlers is not executed right after the event was first
triggered, but rather when it is stopped.
Synchronicity for sustained events is more complex: the at-clause is handled asynchronously iff both
the emission and the handler are asynchronous, whereas the onleave-clause is handled asynchronously
iff the emission was synchronous. Be warned, but do not depend on this, as in the future we might
change this.
Asynchronous Trigger
var e = Event.new()|; at (e?(var v)) { echo("a"+v); sleep(20ms); echo("b"+v) } onleave { echo("c"+v); sleep(20ms); echo("d"+v) }; var handler = e.trigger(1) | echo("triggered"); [00000001] *** triggered [00000002] *** a1 [00000102] *** b1 sleep(200ms); handler.stop() | echo("stopped"); [00000301] *** stopped sleep(25ms); [00000302] *** c1 [00000402] *** d1 // at and onleave clauses may overlap. handler = e.trigger(2) | handler.stop(); sleep(25ms); [00000001] *** a2 [00000002] *** c2 sleep(25ms); [00000201] *** b2 [00000202] *** d2 handler = e.syncTrigger(3) | echo("triggered"); [00000002] *** a3 [00000102] *** b3 [00000001] *** triggered handler.stop() | echo("stopped"); [00000302] *** c3 [00000402] *** d3 [00000301] *** stopped
|
Synchronous Trigger
var e = Event.new()|; at sync (e?(var v)) { echo("a"+v); sleep(20ms); echo("b"+v) } onleave { echo("c"+v); sleep(20ms); echo("d"+v) }; var handler = e.trigger(1) | echo("triggered"); // No need to sleep. [00000002] *** a1 [00000102] *** b1 [00000001] *** triggered handler.stop() | echo("stopped"); [00000301] *** stopped sleep(25ms); [00000302] *** c1 [00000402] *** d1 // at and onleave clauses don’t overlap. handler = e.trigger(2) | handler.stop(); sleep(25ms); [00000001] *** a2 [00000201] *** b2 [00000002] *** c2 [00000202] *** d2 handler = e.syncTrigger(3) | echo("triggered"); [00000002] *** a3 [00000102] *** b3 [00000001] *** triggered handler.stop() | echo("stopped"); [00000302] *** c3 [00000402] *** d3 [00000301] *** stopped
|
An Event is created like any other object. The constructor takes no argument.
var e = Event.new();
[00000001] Event_0x9ad8118
- ’<<’(that)
Watch a that event status and reproduce it on itself, return this. This operator is similar
to an optimized ||= operator. Do not make events watch for themselves, directly or
indirectly.
var e3 = Event.new()|;
var e4 = Event.new()|;
var e_watch = Event.new() << e3 << e4 |;
at (e_watch?)
echo("!");
e3!;
[00000006] *** !
e4!;
[00000007] *** !
- ’||’(that)Logical “or” on events: a new Event that triggers whenever this or that
triggers.
var e1 = Event.new()|;
var e2 = Event.new()|;
var either = e1.’||’(e2)|;
at (either?)
echo("!");
e1!;
[00000004] *** !
e2!;
[00000005] *** !
- asEvent
Return this.
- emit(args[])
Fire an “instantaneous” and “asynchronous” Event. This function is called by the !
operator. It takes any number of arguments, passed to the receiver when the event is
caught.
var e = Event.new()|;
// No handler, lost message.
e.emit();
at (e?) echo("e");
at (e?()) echo("e()");
at (e?(var x)) echo("e(%s)" % [x]);
at (e?(var x, var y)) echo("e(%s, %s)" % [x, y]);
// This is what e! does.
e.emit();
[00000135] *** e
[00000135] *** e()
// This is what e!() does: same as e!.
e.emit();
[00000138] *** e
[00000138] *** e()
// This is what e!(1, [2]) does.
e.emit(1, [2]);
[00000141] *** e
[00000141] *** e(1, [2])
// This is what e!(1, [2], "three") does.
e.emit(1, [2], "three");
[00000146] *** e
To sustain an event, see trigger. See Section 21.15.2 and syncEmit for details about the
synchronicity of the handling.
- hasSubscribers
Return true if at least one subscriber is registered to this event.
var ev = Event.new()|;
ev.hasSubscribers;
[00000000] false
tag: at(ev?) echo(1);
ev.hasSubscribers;
[00000000] true
tag.stop();
ev.hasSubscribers;
[00000000] false
- onEvent(guard, enter, leave, sync)
This is the low-level routine used to implement the |at— construct. Indeed,
at (e? if cond) enter onleave leave is (roughly) translated into
e
.onEvent(
closure (var ’$event’, var ’$payload’) { cond },
closure (var ’$event’, var ’$payload’, var ’$pattern’) { enter },
closure (var ’$event’, var ’$payload’, var ’$pattern’) { leave },
false)
where the false would be true in case of an at sync construct. The cond discards the event iff
it returns void.
var e = Event.new()|;
e.onEvent(
function (var args[]) { echo("cond 1") | true },
function (var args[]) { echo("enter 1") },
function (var args[]) { echo("leave 1") },
true);
e.onEvent(
function (var args[]) { echo("cond 2") },
function (var args[]) { echo("enter 2") },
function (var args[]) { echo("leave 2") },
true);
e.emit(12);
[00001619] *** cond 1
[00001619] *** enter 1
[00001619] *** leave 1
[00001619] *** cond 2
var h = e.trigger()|;
[00001620] *** cond 1
[00001620] *** enter 1
[00001620] *** cond 2
h.stop();
[00001621] *** leave 1
This function is internal and it might change in the future.
- onSubscribe
You can optionally assign an Event to this slot. In this case, it is triggered each time some code
starts watching this event (by setting up an at or a waituntil on it for instance). This slot
cannot be inherited; it defaults to void.
var e = Event.new()|;
assert (e.onSubscribe.isVoid());
e.onSubscribe = Event.new()|;
at (e.onSubscribe?)
echo("new subscriber");
at (e?(12)) {};
[00000001] *** new subscriber
waituntil(e?(15)),
[00000002] *** new subscriber
The following example shows how to set up a clock event that is triggered only on the first “use”
(subscription).
var clock = Event.new()|;
clock.onSubscribe = Event.new()|;
at (clock.onSubscribe? if clock.subscribers.size == 1)
{
echo("arming clock event");
disown({ every|(1s) clock!});
};
echo("ready");
[00000000] *** ready
at (clock?)
echo("tick");
sleep(1.5s);
[00000001] *** arming clock event
[00000002] *** tick
[00000003] *** tick
- subscribe(subscription)
Register a Subscription to be called each time the event triggers. Using a subscription object
gives you more control on on often and how your code will be evaluated.
- subscribers
The list of subscriptions attached to this event.
- syncEmit(args[])
Same as emit but require a synchronous handling. See Section 21.15.2 for details.
- syncTrigger(args[])
Same as trigger but the call will be synchronous (see Section 21.15.2). The stop
method of the handler object will be synchronous as well. See Section 21.15.3 for
examples.
- trigger(args[])
Fire a sustained event (for an unknown amount of time) and return a handler object whose stop
method stops the event. This method is asynchronous and the stop call will be asynchronous as
well. See Section 21.15.3 for examples.
21.16 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 20.8) 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 21.16.3.2 for their
documentation.
- backtrace
The call stack at the moment the exception was thrown (not created), as a List of
StackFrames, from the innermost to the outermost call. Uses Traceable.backtrace.
//#push 1 "file.u"
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";
};
};
//#pop
- location
The location from which the exception was thrown (not created).
System.eval("1/0");
[00090441:error] !!! 1.1-3: /: division by 0
[00090441:error] !!! called from: eval
try
{
System.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";
In the following, since these slots are actually Objects, what is presented as arguments to the slots
are actually arguments to pass to the constructor of the corresponding exception type.
- Argument(routine, index, exception)
During the call of routine, the instantiation of the index-nth argument has thrown an
exception.
Exception.Argument
.new("myRoutine", 3, Exception.Type.new("19/11/2010", Date))
.asString()
== "myRoutine: argument 3: unexpected \"19/11/2010\", expected a Date";
- ArgumentType(routine, index, effective, expected)
Deprecated exception that derives from Type. The routine was called with a index-nth
argument of type effective instead of expected.
Exception.ArgumentType
.new("myRoutine", 1, "hisResult", "Expectation")
.asString()
== "myRoutine: argument 1: unexpected \"hisResult\", expected a String";
[00000003:warning] !!! ‘Exception.ArgumentType’ is deprecated
- Arity(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).asString()
== "myRoutine: expected between 10 and 23 arguments, given 1";
- BadInteger(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).asString()
== "myRoutine: bad integer: 12";
- BadNumber(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).asString()
== "myRoutine: bad number: 12.34";
- Constness
An attempt was made to change a constant value.
Exception.Constness.new().asString()
== "cannot modify const slot";
- Duplicate(fmt, name)
A duplication was made. Use the format fmt to create an error message from name.
Exception.Duplicate.new("duplicate dictionary key", "key").asString()
== "duplicate dictionary key: \"key\"";
- FileNotFound(name)
The file named name cannot be found.
Exception.FileNotFound.new("foo").asString()
== "file not found: foo";
- Lookup(object, name)
A failed name lookup was performed on object to find a slot named name. Suggest what the user
might have meant if Exception.Lookup.fixSpelling is true (which is the default).
Exception.Lookup.new(Object, "GetSlot").asString()
== "lookup failed: Object";
- MatchFailure
A pattern matching failed.
Exception.MatchFailure.new().asString()
== "pattern did not match";
- NegativeNumber(routine, effective)
The routine was called with a negative number (effective). Derives from BadNumber.
Exception.NegativeNumber.new("myRoutine", -12).asString()
== "myRoutine: unexpected -12, expected non-negative number";
- NonPositiveNumber(routine, effective)
The routine was called with a non-positive number (effective). Derives from BadNumber.
Exception.NonPositiveNumber.new("myRoutine", -12).asString()
== "myRoutine: unexpected -12, expected positive number";
- Primitive(routine, msg)
The built-in routine encountered an error described by msg.
Exception.Primitive.new("myRoutine", "cannot do that").asString()
== "myRoutine: cannot do that";
- Redefinition(name)
An attempt was made to refine a slot named name.
Exception.Redefinition.new("foo").asString()
== "slot redefinition: foo";
- Scheduling(msg)
Something really bad has happened with the Urbi task scheduler.
Exception.Scheduling.new("cannot schedule").asString()
== "cannot schedule";
- Syntax(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")
.asString()
== "file.u:14.25: syntax error: unexpected pouCharque";
try
{
System.eval("1 / / 0");
}
catch (var e)
{
assert
{
e.isA(Exception.Syntax);
e.loc.asString() == "1.5";
e.input == "1 / / 0";
e.message == "unexpected /";
}
};
- TimeOut
Used internally to implement the timeout construct (Listing 20.10.7).
Exception.TimeOut.new().asString()
== "timeout expired";
- Type(effective, expected)
A value of type effective was received, while a value of type expected was expected.
Exception.Type.new("hisResult", "Expectation").asString()
== "unexpected \"hisResult\", expected a String";
- UnexpectedVoid
An attempt was made to read the value of void.
Exception.UnexpectedVoid.new().asString()
== "unexpected void";
var a = void;
a;
[00000016:error] !!! unexpected void
[00000017:error] !!! lookup failed: a
21.17 Executable
This class is used only as a common ancestor to Primitive and Code.
There is no point in constructing an Executable.
- asExecutable
Return this.
21.18 File
Files may be created from a String, or from a Path. Using new, 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).
File.new("file.txt");
[00000001:error] !!! new: no such file or directory: file.txt
File.create("file.txt");
[00000002] File("file.txt")
File.new(Path.new("file.txt"));
[00000003] File("file.txt")
You may use InputStream and OutputStream to read or write to Files.
- asList
Read the file, and return its content as a list of its lines. Lines can be Unix-style (ended with \n)
or DOS-style (ended with \r\n).
File.save("file.txt", "1\n2\n" "3\r\n4\r\n");
assert(File.new("file.txt").asList() == ["1", "2", "3", "4"]);
An error is throw if the file cannot be opened, for instance if it is unreadable (Path.readable).
File.save("file.txt", "1\n2\n");
System.system("chmod a-r file.txt")|;
File.new("file.txt").asList();
[00000003:error] !!! asList: file not readable: file.txt
- asPath
A Path being the path of the file.
var file1 = File.create("file");
file1.asPath() == Path.new("file");
var dir = Directory.create("dir");
var file2 = File.create("dir/file");
file2.asPath() == Path.new("dir/file");
- asPrintable
A readable description of this.
File.save("file.txt", "1\n2\n");
assert(File.new("file.txt").asPrintable() == "File(\"file.txt\")");
- asString
The name of the opened file.
File.save("file.txt", "1\n2\n");
assert(File.new("file.txt").asString() == "file.txt");
- basename
A String containing the path of the file without its dirname. Deprecated, use asPath.basename
instead.
var dir = Directory.create("dir")|;
var file1 = File.create("dir/file")|;
var file2 = File.create("file")|;
assert
{
file1.basename == file1.asPath().basename == "file";
file2.basename == file2.asPath().basename == "file";
};
[00000808:warning] !!! ‘basename’ is deprecated, use ‘asPath().basename’
[00000811:warning] !!! ‘basename’ is deprecated, use ‘asPath().basename’
- content
The content of the file as a Binary object.
File.save("file.txt", "1\n2\n");
assert
{
File.new("file.txt").content == Binary.new("", "1\n2\n");
};
- copy(filename)
Copy the file to a new file named filename.
File.save("file", "content");
var file = File.new("file");
[00000001] File("file")
var file2 = file.copy("file2");
[00000002] File("file2")
assert(file2.content == file.content);
- copyInto(dirname)
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
Copy file into dirname directory.
var dir = Directory.create("dir")|;
var file = File.create("file")|;
file.copyInto(dir);
[00000001] File("dir/file")
file;
[00000002] File("file")
dir.content;
[00000003] ["file"]
- create(name)
If the file name does not exist, create it and a return a File to it. Otherwise, first empty it. See
OutputStream for methods to add content to the file.
var p = Path.new("create.txt") |
assert (!p.exists);
// Create the file, and put something in it.
var f = File.create(p)|;
var o = OutputStream.new(f)|;
o << "Hello, World!"|;
o.close();
assert
{
// The file exists, with the expect contents.
p.exists;
f.content.data == "Hello, World!";
// If we create is again, it is empty.
File.create(p).isA(File);
f.content.data == "";
};
- lastModifiedDate
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
Return a Date object stating when the file was last modified. Deprecated, use
asPath.lastModifiedDate instead.
- moveInto(dirname)
This feature is experimental. It might be changed in the future. Feedback on its
use would be appreciated.
Move file into dirname directory.
var dir = Directory.create("dir")|;
var file = File.create("file")|;
file.moveInto(dir);
[00000001] File("dir/file")
file;
[00000001] File("dir/file")
dir.content;
[00000001] ["file"]
- remove
Remove the current file. Returns void.
var p = Path.new("file.txt");
!p.exists;
var f = File.create(p);
p.exists;
f.remove().isVoid;
!p.exists;
- rename(name)
Rename the file to name. If the target exists, it is replaced by the opened file. Return the file
renamed. Deprecated, use asPath.rename instead.
File.save("file.txt", "1\n2\n");
File.new("file.txt").rename("bar.txt");
[00000001:warning] !!! ‘rename(name)’ is deprecated, use ‘asPath().rename(name)’
assert
{
!Path.new("file.txt").exists;
File.new("bar.txt").content.data == "1\n2\n";
};
- save(name, content)
Use create to create the File named name, store the content in it, and close the file. Return
void.
File.save("file.txt", "1\n2\n").isVoid;
File.new("file.txt").content.data == "1\n2\n";
- size
The size of the file, in number of bytes.
File.save("file.txt", "1234").isVoid;
File.new("file.txt").size == 4;
21.19 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 this invalid constructs,
class o2
{
protos = [Finalizable];
function finalize()
{
echo("Ouch");
}
}|;
[00000010:error] !!! apply: cannot inherit from a Finalizable without being one
write:
class o1 : Finalizable
{
function finalize()
{
echo("Ouch");
}
}|;
// Or
var o3 =
do (Finalizable.new())
{
function finalize()
{
echo("Ouch");
}
}|;
If you need multiple prototypes, do as follows.
import Global.*;
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
21.20 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;
// This is actually a call to the unary ’+’.
+1 == 1;
0.123456 == 123456 / 1000000;
1e3 == 1000;
1e-3 == 0.001;
1.234e3 == 1234;
Actually, underscores can be inserted between digits to improve legibility.
123_456.78_90 == 1234567890 / 10_000;
There are also some special numbers, nan, inf.
Math.log(0) == -inf;
Math.exp(-inf) == 0;
(inf/inf).isNan;
A null float can also be obtained with new.
- ’%’(that)
this modulo that.
- ’*’(that)
Product of this by that.
- ’**’(that)
this to the that power (thisthat). The infix exponential operator is right-associative
(Section 20.1.8.1).
2 ** 3 ** 2 == 2 ** (3 ** 2) != (2 ** 3) ** 2;
2 ** 10 == 1024;
2 ** 31 == 2_147_483_648;
-2 ** 31 == -2_147_483_648 == -(2**31);
2 ** 32 == 4_294_967_296;
-2 ** 32 == -4_294_967_296 == -(2**32);
- ’+’(that)
The sum of this and that.
- ’-’(that)
this subtracted by that.
- ’/’(that)
The quotient of this divided by that.
50 / 10 == 5; 10 / 50 == 0.2;
- ’<’(that)
Whether this is less than that. The other comparison operators (<=, >, …) can thus also be
applied on floats since Float inherits Orderable.
0 < 1; !(1 < 0); !(0 < 0);
- ’<<’(that)
this shifted by that bits towards the left.
- ’==’(that)
Whether this equals that.
1 == 1;
!(1 == 2); !(2 == 1);
- ’>>’(that)
this shifted by that bits towards the right.
- ’^’(that)
Bitwise exclusive or between this and that.
- abs
Absolute value of the target.
(-5).abs() == 5 == 5.abs(); 0.abs() == 0;
- acos
Arccosine of the target.
0.acos() == Float.pi/2;
1.acos() == 0;
- asBool
Whether non null.
0.asBool() === false;
0.1.asBool() === true;
(-0.1).asBool() === true;
Math.inf.asBool() === true;
Math.nan.asBool() === true;
- asFloat
this.
var x = 51;
x.asFloat() === x;
- asin
Arcsine of the target.
- asList
Bounce to seq.
0.asList() == []; 3.asList() == [0, 1, 2];
- asString
A String denoting this.
42.asString() == "42";
42.51.asString() == "42.51";
21_474_836_470.asString() == "21474836470";
4_611_686_018_427_387_904.asString() == "4611686018427387904";
(-4_611_686_018_427_387_904).asString() == "-4611686018427387904";
- atan
Arctangent of this.
0.atan() == 0;
1.atan() == Float.pi/4;
- ’bitand’(that)
The bitwise-and between this and that.
- ’bitor’(that)
Bitwise-or between this and that.
- ceil
The smallest integral value greater than or equal to this. See also floor, round, and trunc.
0.ceil() == 0;
1.4.ceil() == 2; 1.5.ceil() == 2; 1.6.ceil() == 2;
(-1.4).ceil() == -1; (-1.5).ceil() == -1; (-1.6).ceil() == -1;
inf.ceil() == inf; (-inf).ceil() == -inf;
nan.ceil().isNan();
- clone
Return a fresh Float with the same value as the target.
var x = 0;
var y = x.clone();
x == y; x !== y;
- ’compl’
The complement to 1 of the target interpreted as a 32-bit integer.
compl 0 == 0xFFFF_FFFF; compl 0xFFFF_FFFF == 0;
- cos
Cosine of this.
0.cos() == 1; Float.pi.cos() == -1;
- each(fun)
Call the functional argument fun on every integer from 0 to target - 1, sequentially. The number
must be non-negative.
{
var res = [];
3.each(function (i) { res << 100 + i });
res
}
== [100, 101, 102];
{
var res = [];
for(var x : 3) { res << x; sleep(20ms); res << (100 + x); };
res
}
== [0, 100, 1, 101, 2, 102];
{
var res = [];
0.each (function (i) { res << 100 + i });
res
}
== [];
- ’each&’(fun)
Call the functional argument fun on every integer from 0 to target - 1, concurrently. The number
must be non-negative.
{
var res = [];
for& (var x : 3) { res << x; sleep(30ms); res << (100 + x) };
res
}
== [0, 1, 2, 100, 101, 102];
- exp
Exponential of the target.
0.exp() == 1;
1.exp() ∼= 2.71828;
- floor
The largest integral value less than or equal to this. See also ceil, round, and trunc.
0.floor() == 0;
1.4.floor() == 1; 1.5.floor() == 1; 1.6.floor() == 1;
(-1.4).floor() == -2; (-1.5).floor() == -2; (-1.6).floor() == -2;
Math.inf.floor() == Math.inf; (-Math.inf).floor() == -inf;
Math.nan.floor().isNan;
- format(finfo)
Format according to the FormatInfo object finfo. The precision, finfo.precision, sets the
maximum number of digits after decimal point when in fixed or scientific mode, and in total
when in default mode. Beware that 0 plays a special role, as it is not a “significant”
digit.
Windows Issues
Under Windows the behavior differs slightly.
"%1.0d" % 0.1 == "0.1";
"%1.0d" % 1.1 == {if (System.Platform.isWindows) "1.1" else "1"};
"%1.0f" % 0.1 == "0";
"%1.0f" % 1.1 == "1";
Conversion to hexadecimal requires this to be integral.
"%x" % 42 == "2a";
"%x" % 0xFFFF == "ffff";
"%x" % 0.5;
[00000005:error] !!! %: expected integer: 0.5
- fresh
Return a new integer at each call.
{
var res = [];
for (var i: 10)
res << Float.fresh();
assert (res == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
res = [];
for (var i: 10)
res << Float.fresh();
assert (res == [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
};
- hash
A Hash object corresponding to this float value. Equal floats (in the sense of ’==’) have equal
hashes. See Object.hash.
0.hash().isA(Hash);
0.hash() == 0.hash() != 1.hash();
- hex
A String with the conversion of this in hexadecimal. Requires this to be integral.
0.hex() == "0";
0xFF.hex() == "ff";
0xFFFF.hex() == "ffff";
65535.hex() == "ffff";
0xffff_ffff.hex() == "ffffffff";
0.5.hex();
[00000005:error] !!! format: expected integer: 0.5
- inf
The infinity.
2**2**2**2**2 == inf;
inf != -inf;
inf == - -inf;
inf * inf == inf;
inf * -inf == -inf;
inf * -2 == -inf;
- isInf
Whether this is infinite.
!0.isInf(); !1.isInf(); !(-1).isInf();
!Math.nan.isInf();
inf.isInf(); (-inf).isInf();
- isNan
Whether is NaN.
!0.isNan(); !1.isNan(); !(-1).isNan();
!inf.isNan(); !(-inf).isNan();
nan.isNan();
- limits
See Float.limits.
- log
The logarithm of the target.
0.log() == -inf;
1.log() == 0;
1.exp().log() == 1;
- max(arg1, ...)
Bounces to List.max on [this, arg1, ...].
1.max() == 1;
1.max(2, 3) == 3;
3.max(1, 2) == 3;
- min(arg1, ...)
Bounces to List.min on [this, arg1, ...].
1.min() == 1;
1.min(2, 3) == 1;
3.min(1, 2) == 1;
- nan
The “not a number” special float value. More precisely, this returns the “quiet NaN”, i.e., it is
propagated in the various computations, it does not raise exceptions.
Float.nan;
[00000000] nan
inf / inf;
[00000000] nan
A NaN has one distinctive property over the other Floats: it is equal to no other
float, not even itself. This behavior is mandated by the IEEE 754-2008 standard.
!(nan === nan); !(nan == nan); nan != nan;
var n = Float.nan;
n === n; n != nan;
Use isNan to check if a Float is NaN.
(inf/inf).isNan; (inf/-inf).isNan;
(nan / nan).isNan;
(0 * nan).isNan; (0 * nan).isNan;
- pi
π.
Float.pi.cos() ** 2 + Float.pi.sin() ** 2 == 1;
- random
A random integer between 0 (included) and the target (excluded).
20.map(function (dummy) { 5.random() });
[00000000] [1, 2, 1, 3, 2, 3, 2, 2, 4, 4, 4, 1, 0, 0, 0, 3, 2, 4, 3, 2]
- round
The integral value nearest to this rounding half-way cases away from zero. See also ceil, floor,
and trunc.
0.round() == 0;
1.4.round() == 1; 1.5.round() == 2; 1.6.round() == 2;
(-1.4).round() == -1; (-1.5).round() == -2; (-1.6).round() == -2;
inf.round() == inf; (-inf).round() == -inf;
nan.round().isNan();
- seq
The sequence of integers from 0 to this- 1 as a list. this must be non-negative.
0.seq() == []; 3.seq() == [0, 1, 2];
(-1).seq();
[00004586:error] !!! seq: expected non-negative integer: -1
- sign
Return 1 if this is positive, 0 if it is null, -1 otherwise.
(-1664).sign() == -1; 0.sign() == 0; 1664.sign() == 1;
- sin
The sine of the target.
- sqr
Square of the target.
1.5.sqr() == 2.25 == 1.5 ** 2;
32.sqr() == 1024 == 32 ** 2;
- sqrt
The square root of the target.
1024.sqrt() == 32 == 1024 ** 0.5;
- srandom
Initialized the seed used by the random function. As opposed to common usage, you should not
use
{
var now = Date.now().timestamp;
now().srandom();
var list1 = 20.map(function (dummy) { 5.random() });
now().srandom();
var list2 = 20.map(function (dummy) { 5.random() });
assert
{
list1 == list2;
}
};
- tan
Tangent of this.
0.tan() == 0;
(Float.pi/4).tan() ∼= 1;
- times(fun)
Call the functional argument fun this times.
3.times(function () { echo("ping") });
[00000000] *** ping
[00000000] *** ping
[00000000] *** ping
- trunc
The integral value nearest to but no larger in magnitude than this. See also ceil, floor, round.
0.trunc() == 0;
1.4.trunc() == 1; 1.5.trunc() == 1; 1.6.trunc() == 1;
(-1.4).trunc() == -1; (-1.5).trunc() == -1; (-1.6).trunc() == -1;
inf.trunc() == inf; (-inf).trunc() == -inf;
nan.trunc().isNan;
- ’each|’(fun)
Call the functional argument fun on every integer from 0 to target - 1, with tight sequentiality.
The number must be non-negative.
{
var res = [];
3.’each|’(function (i) { res << 100 + i });
res
}
== [100, 101, 102];
{
var res = [];
for|(var x : 3) { res << x; sleep(20ms); res << (100 + x); };
res
}
== [0, 100, 1, 101, 2, 102];
21.21 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.
21.22 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.
FormatInfo.new("%+2.3d");
[00000001] %+2.3d
A formatting pattern must one of the following (brackets denote optional arguments):
- %rank%
- %[rank$]options spec
- %|[rank$]options[spec]|
where:
- rank is an non-null integer which denotes a positional argument: a means to output
arguments in a different order.
- 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 |
|
|
|
When accepted, the format string is decoded, and its features are made available as separate slots
of the FormatInfo object.
do (FormatInfo.new("%5$+’=#06.12X"))
{
assert
{
rank == 5; // 5$
prefix == "+"; // +
group == " "; // ’
alignment == 0; // =
alt == true; // #
pad == "0"; // 0
width == 6; // 6
precision == 12; // .12
uppercase == 1; // X
spec == "x"; // X
};
}|;
Formats that do not conform raise errors.
FormatInfo.new("foo");
[00000001:error] !!! new: format: pattern does not begin with %: foo
FormatInfo.new("%20m");
[00000002:error] !!! new: format: invalid conversion type character: m
FormatInfo.new("%");
[00000003:error] !!! new: format: trailing ‘%’
FormatInfo.new("%ss");
[00062475:error] !!! new: format: spurious characters after format: s
FormatInfo.new("%.ss");
[00071153:error] !!! new: format: invalid width after ‘.’: s
FormatInfo.new("%|-8.2f|%%");
[00034983:error] !!! new: format: spurious characters after 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;
"%5s" % 1 == " 1";
"%=5s" % 2 == " 2 ";
"%-5s" % 3 == "3 ";
- alt
Whether the “alternative” display is requested (‘#’).
FormatInfo.new("%s") .alt == false;
FormatInfo.new("%#s").alt == true;
"%s" % 12.3 == "12.3";
"%#s" % 12.3 == "12.3000";
"%x" % 12 == "c";
"%#x" % 12 == "0xc";
- group
Separator to use for thousands as a String. Corresponds to the ‘’’ option. Currently produces
no effect at all.
FormatInfo.new("%s") .group == "";
FormatInfo.new("%’s").group == " ";
"%d" % 123456 == "%’d" % 123456 == "123456";
- pad
The padding character to use for alignment requests. Defaults to space.
FormatInfo.new("%s") .pad == " ";
FormatInfo.new("%0s").pad == "0";
"%5s" % 1 == " 1";
"%05s" % 1 == "00001";
- pattern
The pattern given to the constructor.
FormatInfo.new("%#’12.8s").pattern == "%#’12.8s";
- precision
When formatting a 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;
"%f" % 12.3 == "12.300000";
"%.0f" % 12.3 == "12";
"%.2f" % 12.3 == "12.30";
"%.8f" % 12.3 == "12.30000000";
- prefix
The string to display before positive numbers. Defaults to empty.
FormatInfo.new("%s") .prefix == "";
FormatInfo.new("% s").prefix == " ";
FormatInfo.new("%+s").prefix == "+";
- rank
In the case of a positional argument, its number, otherwise 0.
FormatInfo.new("%s") .rank == 0;
FormatInfo.new("%2$s") .rank == 2;
FormatInfo.new("%03$s").rank == 3;
FormatInfo.new("%4%") .rank == 4;
"%3$s%2$s%2$s%1$s" % ["bar", "o", "f"]
== "%3%%2%%2%%1%" % ["bar", "o", "f"] == "foobar";
Cannot be null.
FormatInfo.new("%00$s").rank;
[00001243:error] !!! new: format: invalid positional argument: 00
- 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;
FormatInfo.new("%|D|").uppercase == 1;
FormatInfo.new("%|d|").uppercase == -1;
- width
Width requested for alignment.
FormatInfo.new("%s") .width == 0;
FormatInfo.new("%10s") .width == 10;
FormatInfo.new("%-10s").width == 10;
FormatInfo.new("%8.2f").width == 8;
21.23 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 from format strings: they are split into regular strings and formatting patterns
(FormatInfo), and stores them.
Formatter.new("Name:%s, Surname:%s;");
[00000001] Formatter ["Name:", %s, ", Surname:", %s, ";"]
All the patterns are introduced with the percent character (%), and they must conform to a
specific syntax, detailed in the section on the construction of the FormatInfo. To denote
the percent character instead of introducing a formatting-specification, use two percent
characters.
var f = Formatter.new("%10s level: %-4.1f%%");
[00039525] Formatter [%10s, " level: ", %-4.1f, "%"]
for (var d: ["Battery" => 83.3, "Sound" => 60])
echo (f % d.asList());
[00041133] *** Battery level: 83.3%
[00041138] *** Sound level: 60 %
Patterns can either all be non-positional (e.g., %s%s), or all positional (e.g., %1$s%2$s).
Formatter.new("%s%s");
[00371506] Formatter [%s, %s]
Formatter.new("%1$s%2$s");
[00385602] Formatter [%1$s, %2$s]
Formatter.new("%1$s%s");
[00409657:error] !!! new: format: cannot mix positional and non-positional\
arguments: %1$s vs. %s
- asList
Return the content of the formatter as a list of strings and 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).
This operator concatenates regular strings and the strings that are result of asString called on
members of args with the appropriate FormatInfo.
Formatter.new("=>") % [] == "=>";
Formatter.new("=> %s") % [1] == "=> 1";
Formatter.new("Name:%s, Surname:%s;") % ["Foo", "Bar"]
== "Name:Foo, Surname:Bar;";
The arity of the Formatter (i.e., the number of expected arguments) and the size of args must
match exactly.
var f = Formatter.new("%s")|;
f % [];
[00000002:error] !!! %: format: too few arguments
f% ["foo", "bar"];
[00000004:error] !!! %: format: too many arguments
If args is not a List, then the call is equivalent to calling ’%’([args]).
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.
// Some large database of people.
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;
21.24 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.
21.25 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>]
// Bouncing getSlot and updateSlot.
group.value;
[00000000] Group [1, 2, 3]
group.value = 10;
[00000000] Group [10, 10, 10]
// Bouncing to each&.
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"]
- ’<<’(member)
Syntactic sugar for add.
- ’==’(that)
Whether this.members == that.members.
Group.new() == Group.new();
Group.new(1, [2], "foo") == Group.new(1, [2], "foo");
Group.new(1, 2) != Group.new(2, 1);
Group.new(1) != Group.new(2);
- add(member, ...)
Add members to this group, and return this.
var g = Group.new(1, 2);
g.add(3, 4) === g;
g.members == [1, 2, 3, 4];
- asString
Report the asString of the members.
Group.new(1, 2).asString() == "Group [1, 2]";
- 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. See Object.getProperty.
class C
{
var val = 0;
}|;
var a = C.new()|; var b = C.new()|;
var g = Group.new() << a << b|;
g.val->prop;
[00010640:error] !!! property lookup failed: val->prop
a.val->prop = 42|;
g.val->prop;
[00010640:error] !!! property lookup failed: val->prop
b.val->prop = 51|;
assert
{
g.val->prop == Group.new(42, 51);
};
- hasProperty(slot, prop)
Bounce to the members and return a group for the results. See Object.hasProperty.
class C
{
var val = 0;
}|;
var a = C.new()|; var b = C.new()|;
var g = Group.new() << a << b|;
assert
{
g.hasProperty("val", "prop") == Group.new(false, false);
a.val->prop = 21;
g.hasProperty("val", "prop") == Group.new(true, false);
b.val->prop = 42;
g.hasProperty("val", "prop") == Group.new(true, true);
};
- hasSlot(name)
True if and only if all the members have the slot.
var g = Group.new(1, 2);
!g.hasSlot("foo");
g.hasSlot("+");
g + 1 == Group.new(2, 3);
- remove(member, ...)
Remove members from this group, and return this. Non-existing members are silently ignored.
var g = Group.new(1, 2, 1);
g.remove(1, 3) === g == Group.new(2);
g.remove(2) === g == Group.new();
- 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, and return a group for the collected
result. See Object.setProperty.
class C
{
var val = 0;
}|;
var g = Group.new() << C.new() << C.new()|;
assert
{
(g.val->prop = 31) == Group.new(31, 31);
};
- updateSlot(name, value)
Bounced to the members so that this.name = value actually updates the value of the slot name
in the group members.
21.26 Hash
A hash is a condensed, easily comparable representation of another value. They are mainly used to
map Dictionary keys to values.
Equal objects must always have the same hash. Different objects should, as much as possible, have
different hashes.
Objects can be hashed with Object.hash.
Object.new().hash().isA(Hash);
- asFloat
A Float value equivalent to the Hash object. Two hashes have the same Float representation if
and only if they are equal.
var h1 = Object.new().hash()|;
var h2 = Object.new().hash()|;
assert
{
h1.asFloat() == h1.asFloat();
h1.asFloat() != h2.asFloat();
};
- combine(that)
Combine that’s hash with this, and return this. This is used to hash composite objects based
on more primitive object hashes. For instance, an object with two slots could be hashed by
hashing its first one, and combining the second one.
class C
{
function init(var a, var b)
{
var this.a = a;
var this.b = b;
};
function hash()
{
this.a.hash().combine(b)
};
}|
assert
{
C.new(0, 0).hash() == C.new(0, 0).hash();
C.new(0, 0).hash() != C.new(0, 1).hash();
};
21.27 InputStream
InputStreams are used to read (possibly binary) files by hand. 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.
InputStream.new(0);
[00000919:error] !!! new: argument 1: unexpected 0, expected a File
File.save("file.txt", "1\n2\n");
var i1 = InputStream.new(File.new("file.txt"));
[00001208] InputStream_0x1046d16e0
Cloning a closed Stream is valid, but it is forbidden to clone an opened one.
var i2 = InputStream.clone().new(File.new("file.txt"));
[00001211] InputStream_0x1045f6760
i1.clone();
[00001288:error] !!! clone: cannot clone opened Streams
Do not forget to close the streams you opened (Section 21.64.2).
- asList
Get the remainder as a List, and an empty List if there is nothing left in the stream. Raise an
error if the file is closed.
var f = "file.txt" |
File.save(f, "1\n2\n") |
var i = InputStream.new(File.new(f)) |
assert (["1", "2"] == i.asList());
assert ([] == i.asList());
i.close();
i.asList();
[00000001:error] !!! asList: stream is closed
- content
Get the remainder as a String, or an empty String if there is nothing left in the stream. Raise
an error if the file is closed.
var f = "file.txt" |
File.save(f, "1\000\n2\n") |
var i = InputStream.new(File.new(f)) |
assert (File.new(f).content.data == i.content());
assert ("" == i.content());
i.close();
i.content();
[00000001:error] !!! content: stream is closed
- get
Get the next available byte as a Float, or nil if the end of file was reached. Raise an error if the
file is closed.
File.save("file.txt", "1\n2\n")|;
var i = InputStream.new(File.new("file.txt"))|;
var x;
while (!(x = i.get()).isNil)
cout << x;
[00000001:output] 49
[00000002:output] 10
[00000003:output] 50
[00000004:output] 10
i.close();
i.get();
[00000005:error] !!! get: stream is closed
- getChar
Get the next available byte as a String, or nil if the end of file was reached. Raise an error if
the file is closed.
File.save("file.txt", "1\n2\n")|;
var i = InputStream.new(File.new("file.txt"))|;
var x;
while (!(x = i.getChar()).isNil)
cout << x;
[00000001:output] "1"
[00000002:output] "\n"
[00000003:output] "2"
[00000004:output] "\n"
i.close();
i.getChar();
[00000005:error] !!! getChar: stream is closed
- getLine
Get the next available line as a String, or nil if the end of file was reached. The end-of-line
characters are trimmed. Raise an error if the file is closed.
File.save("file.txt", "1\n2\n")|;
var i = InputStream.new(File.new("file.txt"))|;
var x;
while (!(x = i.getLine()).isNil)
cout << x;
[00000001:output] "1"
[00000002:output] "2"
i.close();
i.getLine();
[00000005:error] !!! getLine: stream is closed
21.28 IoService
An IoService is used to manage the various operations of a set of Socket.
All Socket and Server are by default using the default IoService which is polled regularly by the
system.
Using a different IoService is required if you need to perform synchronous read operations.
The Socket must be created by the 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.
// Make a simple hello server.
var serverPort = 0|
do (Server.new())
{
listen("127.0.0.1", "0");
lobby.serverPort = port;
at (connection?(var s))
{
s.write("hello");
}
}|;
// Connect to it using our socket.
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/pollFor/pollOneFor
functions of io.
sleep(200ms);
s.isConnected(); // Nothing was received yet
[00000001] true
io.poll();
[00000002] *** received something
sleep(200ms);
A IoService is constructed with no argument.
- makeServer
Create and return a new Server using this IoService.
- makeSocket
Create and return a new Socket using this IoService.
- poll
Handle all pending socket operations(read, write, accept) that can be performed without
waiting.
- pollFor(duration)
Will block for duration seconds, and handle all ready socket operations during this period.
- pollOneFor(duration)
Will block for at most duration, and handle the first ready socket operation and
immediately return.
21.29 Job
Jobs are independent threads of executions. Jobs can run concurrently. They can also be managed
using Tags.
A Job is typically constructed via Control.detach, Control.disown, or Code.spawn.
detach(sleep(10));
[00202654] Job<shell_7>
disown(sleep(10));
[00204195] Job<shell_8>
function () { sleep(10) }.spawn(false);
[00274160] Job<shell_9>
- asJob
Return this.
Job.current.asJob() === Job.current;
- asString
The string Job<name> where name is the name of the job.
Job.current.asString() == "Job<shell>";
var a = function () { sleep(1) }.spawn(false);
a.asString() == "Job<" + a.name + ">";
- backtrace
The current backtrace of the job as a List of StackFrames starting from innermost, to
outermost. Uses Traceable.backtrace.
//#push 100 "foo.u"
function innermost () { Job.current.backtrace }|;
function inner () { innermost() }|;
function outer () { inner() }|;
function outermost () { outer() }|;
echoEach(outermost());
[01234567] *** foo.u:100.25-45: backtrace
[00001732] *** foo.u:101.25-35: innermost
[00001732] *** foo.u:102.25-31: inner
[00001732] *** foo.u:103.25-31: outer
[00001732] *** foo.u:104.10-20: outermost
//#pop
//#push 1 "file.u"
var s = detach(sleep(1))|;
// Leave some time for s to be started.
sleep(100ms);
assert
{
s.backtrace[0].asString() == "file.u:1.16-23: sleep";
s.backtrace[1].asString() == "file.u:1.9-24: detach";
};
// Leave some time for sleep to return.
sleep(1);
assert
{
s.backtrace.size == 0;
};
//#pop
In the case of events, the backtrace is composite: the bottom part corresponds to the location of
the event handler, and the top part is the location of the event emission. The event emission is
denoted by Event.emit rather than !, and its reception by Event.onEvent rather than ? or
at.
//#push 1 "file.u"
var e = Event.new()|;
function showBacktrace(var where)
{
echo("==================== " + where)|
echoEach(Job.current.backtrace);
}|;
at (e?)
showBacktrace("at-enter")
onleave
showBacktrace("at-leave");
e!;
[00000647] *** ==================== at-enter
[01234567] *** file.u:5.12-32: backtrace
[00000647] *** file.u:8.3-27: showBacktrace
[00000647] *** file.u:11.1: emit
[00000647] *** ---- event handler backtrace:
[00000647] *** file.u:7.1-10.27: onEvent
[00000647] *** ==================== at-leave
[01234567] *** file.u:5.12-32: backtrace
[00000647] *** file.u:10.3-27: showBacktrace
[00000647] *** file.u:11.1: emit
[00000647] *** ---- event handler backtrace:
[00000647] *** file.u:7.1-10.27: onEvent
//#push 1 "file.u"
var f = Event.new()|;
function watchEvent()
{
at (f?)
showBacktrace("at-enter")
onleave
showBacktrace("at-leave");
}|;
function sendEvent()
{
f!;
}|;
watchEvent();
sendEvent();
[00000654] *** ==================== at-enter
[01234567] *** file.u:5.12-32: backtrace
[00000654] *** file.u:5.5-29: showBacktrace
[00000654] *** file.u:11.3: emit
[00000654] *** file.u:14.1-11: sendEvent
[00000654] *** ---- event handler backtrace:
[00000654] *** file.u:4.3-7.29: onEvent
[00000654] *** file.u:13.1-12: watchEvent
[00000654] *** ==================== at-leave
[01234567] *** file.u:5.12-32: backtrace
[00000654] *** file.u:7.5-29: showBacktrace
[00000654] *** file.u:11.3: emit
[00000654] *** file.u:14.1-11: sendEvent
[00000654] *** ---- event handler backtrace:
[00000654] *** file.u:4.3-7.29: onEvent
[00000654] *** file.u:13.1-12: watchEvent
- clone
Cloning a job is forbidden.
- current
The Job in charge of executing this “thread” of code.
var j = Job.current|;
assert { j.isA(Job) };
var j1; var j2;
// Both sequential compositions use the same job: the current one.
{ j1 = Job.current; j2 = Job.current }|;
assert { j1 == j2; j1 == j };
{ j1 = Job.current | j2 = Job.current }|;
assert { j1 == j2; j1 == j };
// Concurrency requires several jobs: j1 and j2 are different.
//
// As an optimization, the current job is used for one of these
// jobs. This is an implementation detail, do not rely on it.
{ j1 = Job.current & j2 = Job.current }|;
assert { j1 != j2; j1 == j; j2 != j };
{ j1 = Job.current, j2 = Job.current }|;
assert { j1 != j2; j1 != j; j2 == j };
- dumpState
Pretty-print the state of the job.
//#push 1 "file.u"
var t = detach(sleep(1))|;
// Leave some time for s to be started.
sleep(100ms);
t.dumpState();
[00004295] *** Job: shell_14
[00004295] *** State: sleeping
[00004295] *** Time Shift: 0.003ms
[00004295] *** Tags:
[00004295] *** Tag<Lobby_3>
[00004297] *** Backtrace:
[00004297] *** file.u:1.16-23: sleep
[00004297] *** file.u:1.9-24: detach
//#pop
- jobs
All the existing jobs as a List.
Job.jobs.isA(List);
Job.current in Job.jobs;
- name
The name of the job as a String.
Job.current.name;
[00004293] "shell"
detach(sleep(1)).name;
[00004297] "shell_15"
- interruptible
Return a boolean telling if the job is interruptible (see System.nonInterruptible)
- frozen
Return true if the job is frozen.
- resetStats
Reinitialize the stats computation.
- stats
Return a Dictionary containing information about the execution cycles of Urbi. This is an
internal feature made for developers, it might be changed without notice. See also
resetStats.
var j = detach({
// Each ’;’ increment the Cycles with their execution time.
var i = 0;
{
// Increment the Waiting time.
waituntil(i == 1);
// Increment the Sleeping time.
sleep(100ms);
i = 2;
}, // Will fork and join a child job.
sleep(200ms);
i = 1;
waituntil(i != 1);
// Stop breaks the workflow of each job tagged with the tag.
var t = Tag.new();
t: t.stop(21);
})|;
j.waitForTermination();
var stats = j.stats|;
Float.epsilonTilde = 0.01 |;
assert
{
0 < stats["Cycles"];
stats["CyclesMin"] <= stats["CyclesMean"] <= stats["CyclesMax"];
stats["WaitingMin"] <= stats["WaitingMean"] <= stats["WaitingMax"] ∼= 100ms;
stats["SleepingMin"] <= stats["SleepingMean"] <= stats["SleepingMax"] ∼= 200ms;
stats["Fork"] == stats["Join"] == 1;
stats["WorkflowBreak"] == 1;
stats["TerminatedChildrenWaitingMean"] ∼= 200ms;
stats["TerminatedChildrenSleepingMean"] ∼= 100ms;
};
- status
A String that describes the current status of the job (starting, running, …), and its properties
(frozen, …).
var t = detach(sleep(10));
Job.current.status == "running (current job)";
t.status == "sleeping" ;
- tags
The list of Tags that manage this job.
- terminate
Kill this job.
var r = detach({ sleep(1s); echo("done") })|;
assert (r in Job.jobs);
r.terminate();
assert (r not in Job.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
- waitForTermination
Wait for the job to terminate before resuming execution of the current one. If the job has already
terminated, return immediately.
21.30 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, 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.
// copy.
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.
- noop
Do nothing. Use {} instead.
- ping
Return time verbosely, see 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.
- size(list)
Obsolete syntax for list.size, see 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.
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.
21.31 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 21.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.
// This function supposedly performs expensive computations.
function heavy_computation()
{
echo("Heavy computation");
return 1 + 1;
}|;
// We want to do the heavy computations only if needed,
// and make it a lazy value to be able to evaluate it "on demand".
var v = Lazy.new(closure () { heavy_computation() });
[00000000] heavy_computation()
/* some code */;
// So far, the value was not needed, and heavy_computation
// was not evaluated.
/* some code */;
// If the value is needed, heavy_computation is evaluated.
v.value();
[00000000] *** Heavy computation
[00000000] 2
// If the value is needed a second time, heavy_computation
// is not reevaluated.
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 21.4.1.1.
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. 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;
} |
// The function to cache.
var inc = function(x) { echo("incing " + x) | x+1 } |
// The function with cache. UnaryLazy simply takes the function as argument.
var p = UnaryLazy.new(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 20.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 () { /* Value to make lazy */ 0 });
[00000000] 0
- ’==’(that)
Whether this and that are the same source code and value (an not yet evaluated 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 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.
21.32 List
Lists implement possibly-empty ordered (heterogeneous) collections of objects.
- Container
- RangeIterable
Therefore lists also support RangeIterable.all and RangeIterable.any.
// Are all elements positive?
! [-2, 0, 2, 4].all(function (e) { 0 < e });
// Are all elements even?
[-2, 0, 2, 4].all(function (e) { e % 2 == 0 });
// Is there any even element?
! [-3, 1, -1].any(function (e) { e % 2 == 0 });
// Is there any positive element?
[-3, 1, -1].any(function (e) { 0 < e });
- Orderable
Lists can be created with their literal syntax: a possibly empty sequence of expressions in square
brackets, separated by commas. Non-empty lists may end with a comma (Section 20.1.6.5).
[]; // The empty list
[00000000] []
[1, "2", [3,],];
[00000000] [1, "2", [3]]
However, new can be used as expected.
List.new();
[00000001] []
[1, 2, 3].new();
[00000002] [1, 2, 3]
- ’*’(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] !!! *: argument 1: expected non-negative integer: -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];
[] + [] == [];
The target is left unmodified (contrary to ’+=’).
var l = [1, 2, 3];
l + l == [1, 2, 3, 1, 2, 3];
l == [1, 2, 3];
- ’+=’(that)
Concatenate the contents of the List that to this, and return this. This function modifies its
target, contrary to ’+’. See also ’<<’.
var l = [];
var alias = l;
(l += [1, 2]) == l;
l == [1, 2];
(l += [3, 4]) == l;
l == [1, 2, 3, 4];
alias == [1, 2, 3, 4];
- ’-’(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)
Whether this is less than the that List. This is the lexicographic comparison: this is “less
than” that if, from left to right, one of its member is “less than” the corresponding member of
that:
[0, 0, 0] < [0, 0, 1]; !([0, 0, 1] < [0, 0, 0]);
[0, 1, 2] < [0, 2, 1]; !([0, 2, 1] < [0, 1, 2]);
[1, 1, 1] < [2]; !([2] < [1, 1, 1]);
!([0, 1, 2] < [0, 1, 2]);
or that 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, 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];
- ’<<’(that)
A synonym for insertBack.
- ’==’(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";
["0", "1", "2"][2] == "2";
};
["0", "1", "2"][3];
[00007061:error] !!! []: invalid index: 3
assert
{
["0", "1", "2"][-1] == "2";
["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]|;
assert
{
(f[1] = 42) == 42;
f == [0, 42, 2];
};
for (var i: [0, 1, 2])
f[i] = 10 * f[i];
assert (f == [0, 420, 20]);
- asTree
Display the content of the List as a tree representation.
echo("simple list:" + ["a", "b", ["d", "e", "f", "g"]].asTree());
[00000004] *** simple list:
[
a,
b,
[
d,
e,
f,
g,
]
]
echo("list with dictionary:" +
["a", "b", ["c" => ["d", "e"], "f" => "g"]].asTree());
[00000005] *** list with dictionary:
[
a,
b,
[
c =>
[
d,
e,
]
f => g,
]
]
- append(that)
Deprecated alias for ’+=’.
var one = [1]|;
one.append(["one", [1]]);
[00000005:warning] !!! ‘list.append(that)’ is deprecated, use ‘list += that’
[00000005] [1, "one", [1]]
- 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();
[00000007:error] !!! argMax: 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();
[00000011:error] !!! argMin: list cannot be empty
- asBool
Whether not empty.
[].asBool() == false;
[1].asBool() == true;
- asList
Return the target.
var l = [0, 1, 2];
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();
[00000017:error] !!! back: cannot be applied onto empty list
- clear
Empty the target, return it.
var l = [0, 1, 2];
l.clear() === l == [];
l.clear() === l == [];
- 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
- ’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
- 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
- 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
{
// Keep only odd numbers.
filter(function (v) {v % 2 == 1}) == [1, 3, 5];
// Keep all.
filter(function (v) { true }) == this;
// Keep none.
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 that equals one of the members.
[0, 1, 2].has(1);
! [0, 1, 2].has(5);
The infix operators in and not in use has (see Section 20.1.8.7).
1 in [0, 1];
2 not in [0, 1];
!(2 in [0, 1]);
!(1 not in [0, 1]);
- hash
A Hash object corresponding to this list value. Equal lists (in the sense of ’==’) have equal
hashes, see Object.hash.
[].hash().isA(Hash);
[].hash() == [].hash();
[1, "foo"].hash() == [1, "foo"].hash();
[0, 1].hash() != [1, 0].hash();
- hasSame(that)
Whether that is is physically equal to one of the members.
var x = 1;
[0, x, 2].hasSame(x);
![0, x, 2].hasSame(1);
- head
Synonym for front.
assert([0, 1, 2].head() == 0);
[].head();
[00000000:error] !!! head: cannot be applied onto empty list
- insert(where, what)
Insert what before the value at index where, return this.
var l = [0, 1];
l.insert(0, 10) === l == [10, 0, 1];
l.insert(2, 20) === l == [10, 0, 20, 1];
The index must be valid, to insert past the end, use insertBack.
[].insert(0, "foo");
[00044239:error] !!! insert: invalid index: 0
[1, 2, 3].insert(4, 30);
[00044339:error] !!! insert: invalid index: 4
- insertBack(that)
Insert that at the end of the target, return this.
var l = [0, 1];
l.insertBack(2) === l;
l == [0, 1, 2];
- insertFront(that)
Insert the given element at the beginning of the target. Return this.
var l = [0, 1];
l.insertFront(0) === l;
l == [0, 0, 1];
- insertUnique(that)
If that is not in this, append it. Return this.
var l = [0, 1];
l.insertUnique(0) === l == [0, 1];
l.insertUnique(2) === l == [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.
[].keys == [];
["a", "b", "c"].keys == [0, 1, 2];
This allows uniform iteration over a Dictionary or a List.
var l = ["a", "b", "c"]|;
var res = []|;
for (var k: l.keys)
res << l[k];
assert (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];
[1, 2, 3].map (function (x) { x*2 })
== [2, 4, 6];
- matchAgainst(handler, pattern)
If pattern is a List of same size, use handler to match each member of this against the
corresponding pattern. Return true if the match succeeded, false in other cases.
assert
{
([1, 2] = [1, 2]) == [1, 2];
};
([1, var a] = [1, 2]) == [1, 2];
[00004360] true
assert
{
a == 2;
};
([var u, var v, var w] = [1, 2, 3]) == [1, 2, 3];
[00004376] true
assert
{
[u, v, w] == [1, 2, 3];
};
[1, 2] = [2, 1];
[00005863:error] !!! pattern did not match
[1, var a] = [2, 1];
[00005864:error] !!! pattern did not match
[1, var a] = [1];
[00005865:error] !!! pattern did not match
[1, var a] = [1, 2, 3];
[00005865:error] !!! pattern did not match
- max(comp = function(a, b) { a < b })
Return the “largest” member based on the comparison function comp.
[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] !!! max: list cannot be empty
The members must be comparable.
[0, 2, "a", 1].max();
[00000002:error] !!! max: argument 2: unexpected "a", expected a Float
- min(co;p = function(a, b) { a < b })
Return the “smallest” member based on the comparison function comp.
[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] !!! min: 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];
c.remove(0) === c == [1, 2, 3];
c.remove(42) === 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];
t.removeBack() == 2;
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];
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];
g.removeFront() == 0;
g == [1, 2];
[].removeFront();
[00000000:error] !!! removeFront: cannot be applied onto empty list
- reverse
The target with the order of elements inverted.
[0, 1, 2].reverse() == [2, 1, 0];
- size
The number of elements in this.
[0, 1, 2].size == 3;
[].size == 0;
- sort(comp = function(a, b) { a < b })
A new List with the contents of this, sorted with respect to the comp comparison function.
var l = [3, 0, -2, 1];
l.sort() == [-2, 0, 1, 3];
l == [3, 0, -2, 1];
l.sort(function(a, b) {a.abs() < b.abs()})
== [0, 1, -2, 3];
[2, 1].sort(1);
[00000001:error] !!! unexpected 1, expected a Executable
Following the Garbage In, Garbage Out principle, if comp is not a strict weak ordering (e.g., if
comp(a, b) && comp(b, a) holds for some a and b), the result is meaningless.
[1, 2, 3].sort(function(a, b) { true });
[00011293] [2, 3, 1]
- 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
this 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
- unique
A new List composed of a single (based on == comparison) copy of all the members of this in no
particular order.
[].unique() == [];
[1].unique() == [1];
[1, 1].unique() == [1];
[1, 2, 3, 2, 1].unique() == [1, 2, 3];
- 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];
21.33 Loadable
Loadable objects can be switched on and off — typically physical devices.
The intended use is rather as follows:
import gsrapi.*;
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_0x42364388
m.load;
[00000004] false
m.go(1);
[00000006] *** cannot advance, the motor is off
m.on();
[00000007] Motor_0x42364388
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;
};
};
21.34 Lobby
A lobby is the local environment for each (remote or local) connection to an Urbi server.
Since every lobby is-a Channel, one can use the methods of Channel.
lobby << 123;
[00478679] 123
lobby << "foo";
[00478679] "foo"
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_0x4233b248]
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
- authors
Credit the authors of Urbi SDK. See also thanks and Section 1.4.
- banner
Internal. Display Urbi SDK banner.
- bytesReceived
The number of bytes that were “input” to this. See also receive.
var l = Lobby.create()|;
assert (l.bytesReceived == 0);
l.receive("123456789;");
[00000022] 123456789
assert (l.bytesReceived == 10);
l.receive("1234;");
[00000023] 1234
assert (l.bytesReceived == 15);
- bytesSent
The number of bytes that were “output” by this. See also send and write.
var l = Lobby.create()|;
assert (l.bytesSent == 0);
l.send("0123456789");
[00011988] 0123456789
// 22 = "[00011988] 0123456789\n".size.
assert (l.bytesSent == 22);
l.send("xx", "label");
[00061783:label] xx
// 20 = "[00061783:label] xx\n".size.
assert (l.bytesSent == 42);
- connected
Whether this is connected.
- connectionTag
The tag of all code executed in the context of this. This tag applies to this, but the top-level
loop is immune to Tag.stop, therefore connectionTag controls every thing that was launched
from this lobby, yet the lobby itself is still usable.
every (1s) echo(1), sleep(0.5s); every (1s) echo(2),
sleep(1.2s);
connectionTag.stop();
[00000507] *** 1
[00001008] *** 2
[00001507] *** 1
[00002008] *** 2
"We are alive!";
[00002008] "We are alive!"
every (1s) echo(3), sleep(0.5s); every (1s) echo(4),
sleep(1.2s);
connectionTag.stop();
[00003208] *** 3
[00003710] *** 4
[00004208] *** 3
[00004710] *** 4
"and kicking!";
[00002008] "and kicking!"
Of course, a background job may stop a foreground one.
{ sleep(1.2s); connectionTag.stop(); },
// Note the ‘;’, this is a foreground statement.
every (1s) echo(5);
[00005008] *** 5
[00005508] *** 5
"bye!";
[00006008] "bye!"
- copyright(deep = true)
Display the copyright of Urbi SDK. Include copyright information about sub-components if
deep.
- create
Instantiate a new Lobby.
Lobby.create().isA(Lobby);
- echo(value, channel = "")
Send value.asString to this, prefixed by the String channel name if specified. This is the
preferred way to send informative messages (prefixed with ‘***’).
lobby.echo("111", "foo");
[00015895:foo] *** 111
lobby.echo(222, "");
[00051909] *** 222
lobby.echo(333);
[00055205] *** 333
- echoEach(list, channel = "")
Apply echo(m, channel) for each member m of list.
lobby.echo([1, "2"], "foo");
[00015895:foo] *** [1, "2"]
lobby.echoEach([1, "2"], "foo");
[00015895:foo] *** 1
[00015895:foo] *** 2
lobby.echoEach([], "foo");
- instances
A list of the currently alive lobbies. It contains at least the Lobby object itself, and the current
lobby.
lobby in Lobby.instances;
Lobby in Lobby.instances;
- license
Display the end user license agreement of the Urbi SDK.
- lobby
The lobby of the current connection. This is typically this.
But when several connections are active (e.g., when there are remote connections), it can be
different from the target of the call.
Lobby.create()| Lobby.create()|;
for (var l : lobbies)
assert (l.lobby == Lobby.lobby);
- onDisconnect(lobby)
Event launched when this is disconnected. There is a single event instance for all the lobby,
Lobby.onDisconnect, the disconnected lobby being passed as argument.
- quit
Shut this lobby down, i.e., close the connection. The server is still running, see System.shutdown
to quit the server.
- receive(value)
This is low-level routine. Pretend the String value was received from the connection. There is
no guarantee that value will be the next program block that will be processed: for instance, if
you load a file which, in its middle, uses lobby.receive("foo"), then "foo" will be appended
after the end of the file.
Lobby.create().receive("12;");
[00478679] 12
- remoteIP
When this is connected to a remote server, it’s Internet address.
- send(value, channel = "")
This is low-level routine. Send the String value to this, prefixed by the String channel name
if specified.
lobby.send("111", "foo");
[00015895:foo] 111
lobby.send("222", "");
[00051909] 222
lobby.send("333");
[00055205] 333
- thanks
Credit the contributors of Urbi SDK. See also authors and Section 1.4.
- wall(value, channel = "")
Perform echo(value, channel) on all the existing lobbies (except Lobby itself).
Lobby.wall("111", "foo");
[00015895:foo] *** 111
- write(value)
This is low-level routine. Send the String value to the connection. Note that because of
buffering, the output might not be visible before an end-of-line character is output.
lobby.write("[");
lobby.write("999999999:");
lobby.write("myTag] ");
lobby.write("Hello, World!");
lobby.write("\n");
[999999999:myTag] Hello, World!
21.35 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
21.36 Logger
Logger is used to report information to the final user or to the developer. It allows to pretty print
warnings, errors, debug messages or simple logs. Logger can also be used as Tag objects for it to
handle nested calls indentation. A log message is assigned a category which is shown between brackets
at beginning of lines, and a level which defines the context in which it has to be shown (see
Section 19.1.2). Log level definition and categories filtering can be changed using environment
variables defined in Section 19.1.2.
The proper use of Loggers is to instantiate your own category, and then to use the operator << for log
messages, possibly qualified by the proper level (in increase order of importance: dump, debug, trace,
log, warn, err):
var logger = Logger.new("Category")|;
logger.dump() << "Low level debug message"|;
// Nothing displayed, unless the debug level is set to DUMP.
logger.warn() << "something wrong happened, proceeding"|;
[ Category ] something wrong happened, proceeding
logger.err() << "something really bad happened!"|;
[ Category ] something really bad happened!
You may also directly use the logger and passing arguments to these slots.
Logger.log("message", "Category") |;
[ Category ] message
Logger.log("message", "Category") :
{
Logger.log("indented message", "SubCategory")
}|;
[ Category ] message
[ SubCategory ] indented message
Urbi SDK comes with a number of built-in categories that all belong to one of the four following
category families. Precise categories are provided for information, but there is no guarantee that these
categories will maintained, that their semantics will not change, or that they are all listed
here.
-
Libport
- The Libport library.
-
Sched
- The coroutine library.
-
Serialize
- The serialization library.
-
Urbi
- Everything about Urbi SDK.
-
Urbi.Parser
- Bison parser traces.
-
Urbi.Scanner
- Flex scanner traces.
Logger can be used as is, without being cloned. It is possible to clone Logger defining a category
and/or a level of debug.
Logger.log("foo");
[ Logger ] foo
[00004702] Logger<Logger>
Logger.log("bar", "Category") |;
[ Category ] bar
var l = Logger.new("Category2");
[00004703] Logger<Category2>
l.log("foo")|;
[ Category2 ] foo
l.log("foo", "ForcedCategory") |;
[ ForcedCategory ] foo
- ’<<’(object)
Allow to use the Logger object as a Channel. This slot can only be used if both category
and level have been defined when cloning.
l = Logger.new("Category", Logger.Levels.Log);
[00090939] Logger<Category>
l << "abc";
[ Category ] abc
[00091939] Logger<Category>
- categories
A Dictionary of known categories, mapping their name to their activation status as a Boolean.
Note that changing this dictionary has no influence over the set of active categories, see enable
and disable.
var c = Logger.categories;
c.isA(Dictionary);
c["Urbi.Call"];
// The four category families used by Urbi SDK.
c.keys.map (function (var s) { s.split(".")[0] }).unique()
== ["Libport", "Logger", "Runner", "Sched", "Serialize", "Urbi", "test"];
- debug(message = "", category = category)
Report a debug message of category to the user. It will be shown if the debug level is Debug or
Dump. Return this to allow chained operations.
// None of these are displayed unless the current level is at least DEBUG.
Logger.debug() << "debug 1"|;
Logger.debug("debug 2")|;
Logger.debug("debug 3", "Category2")|;
- disable(pattern)
Disable the categories that match the pattern, i.e., suppress logs about these categories
independently of the current level. Affects existing and future categories. See also enable.
var c = Logger.new("Logger.Foo");
c << "Enabled";
// Enabled by default.
Logger.categories["Logger.Foo"];
// Disable any categories that starts with Logger.Foo, existing or not.
Logger.disable("Logger.Foo*").isVoid;
c << "Disabled";
!Logger.categories["Logger.Foo"];
// A new category, disabled at creation.
var c2 = Logger.new("Logger.Foo.Bar");
c2 << "Disabled";
!Logger.categories["Logger.Foo.Bar"];
- dump(message = "", category = category)
Report a debug message of category to the user. It will be shown if the debug level is Dump.
Return this to allow chained operations.
// None of these are displayed unless the current level is at least DEBUG.
Logger.dump() << "dump 1"|;
Logger.dump("dump 2")|;
Logger.dump("dump 3", "Category2")|;
- enable(pattern)
Enable the categories that match the pattern, i.e., suppress logs about these categories
independently of the current level. Affects existing and future categories. See also disable.
// Disable any categories that starts with Logger.Foo, existing or not.
Logger.disable("Logger.Foo*");
Logger.log("disabled", "Logger.Foo.Bar");
Logger.log("disabled", "Logger.Foo.Baz");
// Enable those that start with Logger.Foo.Bar.
Logger.enable("Logger.Foo.Bar*");
Logger.log("enabled", "Logger.Foo.Bar");
[ Logger.Foo.Bar ] enabled
Logger.log("enabled", "Logger.Foo.Bar.Qux");
[ Logger.Foo.Bar.Qux ] enabled
Logger.log("disabled", "Logger.Foo.Baz");
- err(message = "", category = category)
Report an error message of category to the user. Return this to allow chained
operations.
- init(category)
Define the category of the new Logger object. If no category is given the new Logger will
inherit the category of its prototype.
- level
The current level of verbosity (see Levels). Can be read and assigned to.
// Logger is enabled by default.
Logger.categories["Logger"];
[00000001] true
// Log level is enabled by default.
Logger.level;
[00000002] Log
Logger.log("Logger enabled");
[ Logger ] Logger enabled
// Disable it.
Logger.level = Logger.Levels.None;
Logger.level;
[00000003] None
Logger.log("Logger is disabled");
// Enable it back.
Logger.level = Logger.Levels.Log;
[00000004] Log
Logger.log("Logger enable again");
[ Logger ] Logger enable again
- Levels
An Enumeration of the log levels defined in Section 19.1.2.
Logger.Levels.values;
[00000001] [None, Log, Trace, Debug, Dump]
- log(message = "", category = category)
Report a debug message of category to the user. It will be shown if debug is not disabled.
Return this to allow chained operations.
Logger.log() << "log 1"|;
[ Logger ] log 1
Logger.log("log 2")|;
[ Logger ] log 2
Logger.log("log 3", "Category")|;
[ Category ] log 3
- onEnter
The primitive called when Logger is used as a Tag and is entered. This primitive only increments
the indentation level.
- onLeave
The primitive called when Logger is used as a Tag and is left. This primitive only decrements the
indentation level.
- set(specs)
Enable/disable the categories according to the specs. The specs is a comma-separated list of
categories or globs (e.g., ‘Libport’, ‘Urbi.*’) with optional modifiers ‘+’ or ‘-’. If the modifier is
a ‘+’ or if there is no modifier the category will be displayed. If the modifier is a ‘-’ it will be
hidden.
Modifiers can be chained, accumulated from left to right: ‘-*,+Urbi.*,-Urbi.At’ will only
display categories beginning with ‘Urbi.’ except ‘Urbi.At’, ‘-Urbi.*’ will display all categories
except those beginning with ‘Urbi.’.
Actually, the first character defines the state for unspecified categories: ‘Urbi’ (or ‘+Urbi’)
enables only the ‘Urbi’ category, while ‘-Urbi’ enables everything but ‘Urbi’. Therefore,
‘+Urbi.*,-Urbi.At’ and ‘Urbi.*,-Urbi.At’ are equivalent to ‘-*,+Urbi.*,-Urbi.At’.
var l1 = Logger.new("Logger")|;
var l2 = Logger.new("Logger.Sub")|;
var l3 = Logger.new("Logger.Sub.Sub")|;
function check(specs, s1, s2, s3)
{
Logger.set(specs);
assert
{
Logger.categories["Logger"] == s1;
Logger.categories["Logger.Sub"] == s2;
Logger.categories["Logger.Sub.Sub"] == s3;
};
}|;
check("-L*", false, false, false);
check("L*", true, true, true);
check("-L*,+Lo*", true, true, true);
check("-L*,+Logger", true, false, false);
check("+Logger,-Logger.*", true, false, false);
check("-*,+Logger.*,-Logger.Sub.*", false, true, false);
check("+Logger*,-Logger.*,+Logger.Sub.*", true, false, true);
- trace(message = "", category = category)
Report a debug message of category to the user. It will be shown if the debug level is Trace,
Debug or Dump. Return this to allow chained operations.
// None of these are displayed unless the current level is at least TRACE.
Logger.trace() << "trace 1"|;
Logger.trace("trace 2")|;
Logger.trace("trace 3", "Category2")|;
- warn(message = "", category = category)
Report a warning message of category to the user. Return this to allow chained operations.
Logger.warn() << "warn 1"|;
[ Logger ] warn 1
Logger.warn("warn 2")|;
[ Logger ] warn 2
Logger.warn("warn 3", "Category")|;
[ Category ] warn 3
21.37 Math
This object is 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.
Math is a package, so you can use
to make its Slots visible.
Since it is a Singleton, you are not expected to build other instances.
- abs(float)
Bounce to Float.abs.
Math.abs(1) == Math.abs(-1) == 1;
Math.abs(0) == 0;
Math.abs(3.5) == Math.abs(-3.5) == 3.5;
- acos(float)
Bounce to Float.acos.
Math.acos(0) == Float.pi/2;
Math.acos(1) == 0;
- 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;
- ceil
Bounce to Float.ceil.
Math.ceil(1.4) == Math.ceil(1.5) == Math.ceil(1.6) == Math.ceil(2) == 2;
- cos(float)
Bounce to Float.cos.
Math.cos(0) == 1;
Math.cos(pi) ∼= -1;
- exp(float)
Bounce to Float.exp.
- floor
Bounce to Float.floor.
Math.floor(1) == Math.floor(1.4) == Math.floor(1.5) == Math.floor(1.6) == 1;
- inf
Bounce to Float.inf.
- log(float)
Bounce to Float.log.
- max(arg1, ...)
Bounce to [arg1, ...].max, see List.max.
Math.max( 100, 20, 3 ) == 100;
Math.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.
20.map(function (dummy) { Math.random(5) });
[00000000] [1, 2, 1, 3, 2, 3, 2, 2, 4, 4, 4, 1, 0, 0, 0, 3, 2, 4, 3, 2]
- round(float)
Bounce to Float.round.
Math.round(1 ) == Math.round(1.1) == Math.round(1.49) == 1;
Math.round(1.5) == Math.round(1.51) == 2;
- sign(float)
Bounce to Float.sign.
Math.sign(-2) == -1; Math.sign(0) == 0; Math.sign(2) == 1;
- sin(float)
Bounce to Float.sin.
Math.sin(0) == 0;
Math.sin(pi) ∼= 0;
- sqr(float)
Bounce to Float.sqr.
Math.sqr(1.5) == Math.sqr(-1.5) == 2.25;
Math.sqr(16) == Math.sqr(-16) == 256;
- 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) == Math.trunc(1.1) == Math.trunc(1.49) == Math.trunc(1.5)
== Math.trunc(1.51)
== 1;
21.38 Matrix
The init function is overloaded, and its behavior depends on the number of arguments and their
types.
When there is a single argument, it can either be a List, or another Matrix.
If it’s a List of “Vectors and/or Lists of Floats”, then they must have the same sizes and constitute
the rows.
var listList = Matrix.new([ [0, 1], [0, 1] ]);
[00071383] Matrix([
[0, 1],
[0, 1]])
var listVector = Matrix.new([ [0, 1], Vector.new([0, 1])])|;
var vectorList = Matrix.new([Vector.new([0, 1]), [0, 1] ])|;
var vectorVector = Matrix.new([Vector.new([0, 1]), Vector.new([0, 1])])|;
assert
{
listList == listVector;
listList == vectorList;
listList == vectorVector;
};
Matrix.new([ [0], [1, 2]]);
[00000030:error] !!! new: expecting rows of size 1, got size 2 for row 2
Matrix.new([Vector.new([0]), [1, 2]]);
[00000056:error] !!! new: expecting rows of size 1, got size 2 for row 2
Matrix.new([ [0], Vector.new([1, 2])]);
[00000071:error] !!! new: expecting rows of size 1, got size 2 for row 2
Matrix.new([Vector.new([0]), Vector.new([1, 2])]);
[00052403:error] !!! new: expecting rows of size 1, got size 2 for row 2
If it’s a Matrix, then there is a deep-copy: they are not aliases.
var m1 = Matrix.new([[1, 1], [1, 1]])|;
var m2 = Matrix.new(m1)|;
m2[0, 0] = 0|;
assert
{
m1 == Matrix.new([[1, 1], [1, 1]]);
m2 == Matrix.new([[0, 1], [1, 1]]);
};
When given two Float arguments, they must be the two integers, defining the size of the null
Matrix.
Matrix.new(2, 3);
[00051329] Matrix([
[0, 0, 0],
[0, 0, 0]])
In other cases, the arguments are expected to be Lists of Floats and/or Vectors.
Matrix.new([1, 2]) == Matrix.new([[1, 2]]);
Matrix.new([1, 2], [3, 4]) == Matrix.new([[1, 2], [3, 4]]);
Matrix.new([1, 2], Vector.new(3, 4)) == Matrix.new([[1, 2], [3, 4]]);
These rows must have equal sizes.
// Lists and Lists.
Matrix.new([0], [1, 2]);
[00000160:error] !!! new: expecting rows of size 1, got size 2 for row 2
Matrix.new([0, 1], [2]);
[00000169:error] !!! new: expecting rows of size 2, got size 1 for row 2
// Lists and Vectors.
Matrix.new([0], Vector.new(1, 2));
[00000178:error] !!! new: expecting rows of size 1, got size 2 for row 2
Matrix.new(Vector.new(0, 1), [2]);
[00000186:error] !!! new: expecting rows of size 2, got size 1 for row 2
// Vectors and Vectors.
Matrix.new(Vector.new(0), Vector.new(1, 2));
[00000195:error] !!! new: expecting rows of size 1, got size 2 for row 2
Matrix.new(Vector.new(0, 1), Vector.new(2));
[00000204:error] !!! new: expecting rows of size 2, got size 1 for row 2
- ’*’(that)
If that is a Matrix, matrix product between this and that.
Matrix.new([1, 2]) * Matrix.new([10], [20])
== Matrix.new([50]);
Matrix.new([3, 4]) * Matrix.new([10], [20])
== Matrix.new([110]);
Matrix.new([1, 2], [3, 4]) * Matrix.new([10], [20])
== Matrix.new([50], [110]);
The sizes must be compatible (
this .size.second = that.size.first ).
Matrix.new([1, 2]) * Matrix.new([3, 4]);
[00081168:error] !!! *: incompatible sizes: 1x2, 1x2
If that is a Float, the scalar product.
Matrix.new([1, 2], [3, 4]) * 3 == Matrix.new([3, 6], [9, 12]);
- ’*=’(that)
In place (Section 20.1.8.2) product (’*’). The same constraints apply.
var lhs1 = Matrix.new([1, 2], [3, 4]);
var lhs2 = Matrix.new(lhs1);
var rhs = Matrix.new([10], [20]);
var res = lhs1 * rhs;
(lhs1.’*=’(rhs)) === lhs1; lhs1 == res;
(lhs2 *= rhs ) === lhs2; lhs2 == res;
rhs *= rhs;
[00272182:error] !!! *=: incompatible sizes: 2x1, 2x1
var v = Matrix.new([1, 2], [3, 4]);
var res = v * 3;
(v *= 3) === v; v == res;
- ’+’(that)
The sum of this and that. Their sizes must be equal.
Matrix.new([1, 2]) + Matrix.new([10, 20])
== Matrix.new([11, 22]);
Matrix.new([1, 2], [3, 4]) + Matrix.new([10, 20], [30, 40])
== Matrix.new([11, 22], [33, 44]);
Matrix.new([1, 2]) + Matrix.new([10, 20], [30, 40]);
[00002056:error] !!! +: incompatible sizes: 1x2, 2x2
If that is a Float, the scalar addition.
Matrix.new([1, 2], [3, 4]) + 3 == Matrix.new([4, 5], [6, 7]);
- ’+=’(that)
In place (Section 20.1.8.2) sum (’+’). The same constraints apply.
var lhs1 = Matrix.new([ 1 , 2], [ 3, 4]);
var lhs2 = Matrix.new(lhs1);
var rhs = Matrix.new([10, 20], [30, 40]);
var res = lhs1 + rhs;
(lhs1.’+=’(rhs)) === lhs1; lhs1 == res;
(lhs2 += rhs ) === lhs2; lhs2 == res;
lhs1 += Matrix.new([1, 2]);
[00338194:error] !!! +=: incompatible sizes: 2x2, 1x2
var v = Matrix.new([3, 6], [9, 12]);
var res = v + 3;
(v += 3) === v; v == res;
- ’-’(that)
The difference of this and that. Their sizes must be equal.
Matrix.new([1, 2]) - Matrix.new([10, 20])
== Matrix.new([-9, -18]);
Matrix.new([1, 2], [3, 4]) - Matrix.new([10, 20], [30, 40])
== Matrix.new([-9, -18], [-27, -36]);
Matrix.new([1, 2]) - Matrix.new([10, 20], [30, 40]);
[00002056:error] !!! -: incompatible sizes: 1x2, 2x2
If that is a Float, the scalar difference.
Matrix.new([1, 2], [3, 4]) - 3 == Matrix.new([-2, -1], [0, 1]);
- ’-=’(that)
In place (Section 20.1.8.2) difference (’-’). The same constraints apply.
var lhs1 = Matrix.new([ 1 , 2], [ 3, 4]);
var lhs2 = Matrix.new(lhs1);
var rhs = Matrix.new([10, 20], [30, 40]);
var res = lhs1 - rhs;
(lhs1.’-=’(rhs)) === lhs1; lhs1 == res;
(lhs2 -= rhs ) === lhs2; lhs2 == res;
lhs1 -= Matrix.new([1, 2]) ;
[00362383:error] !!! -=: incompatible sizes: 2x2, 1x2
var v = Matrix.new([3, 6], [9, 12]);
var res = v - 3;
(v -= 3) === v; v == res;
- ’/’(that)
Same as this * that.inverse. that must be invertible.
var lhs = Matrix.new([20, 0], [0, 200]);
var rhs = Matrix.new([10, 0], [0, 100]);
var res = Matrix.new([ 2, 0], [0, 2]);
lhs / rhs == res;
(lhs / rhs) * rhs == lhs;
rhs * (lhs / rhs) == lhs;
lhs / Matrix.createZeros(2, 2);
[00160168:error] !!! /: non-invertible matrix: <<0, 0>, <0, 0>>
If that is a Float, the scalar division.
Matrix.new([3, 6], [9, 12]) / 3 == Matrix.new([1, 2], [3, 4]);
- ’/=’(that)
In place (Section 20.1.8.2) division (’/’). The same constraints apply.
var lhs1 = Matrix.new([20, 0], [0, 200]);
var lhs2 = Matrix.new(lhs1);
var rhs = Matrix.new([10, 0], [0, 100]);
var res = Matrix.new([ 2, 0], [0, 2]);
(lhs1.’/=’(rhs)) === lhs1; lhs1 == res;
(lhs2 /= rhs ) === lhs2; lhs2 == res;
lhs1 /= Matrix.createZeros(2, 2);
[00207285:error] !!! /=: non-invertible matrix: <<0, 0>, <0, 0>>
var v = Matrix.new([3, 6], [9, 12]);
var res = v / 3;
(v /= 3) === v; v == res;
- ’==’(that)
Whether this and that have the same dimensions and members.
do (Matrix)
{
assert
{
new([[1], [2], [3]]) == new([[1], [2], [3]]);
!(new([[1], [2], [3]]) == new([[1], [2]]));
!(new([[1], [2], [3]]) == new([[3], [2], [1]]));
};
}|;
- ’[]’(row, col)
The element at row, col. The index row must verify 0 ≤ row < this
.size.first , or
−this
.size.first ≤ row < 0, in which case it is equivalent to using index row + this
.size.first :
it counts “backward”. Similarly for col.
var m = Matrix.new([1, 2, 3], [10, 20, 30])|;
assert
{
m[0, 0] == 1; m[0, -3] == 1;
m[0, 1] == 2; m[0, -2] == 2;
m[0, 2] == 3; m[0, -1] == 3;
m[1, 2] == 30; m[-1, -1] == 30;
};
m[2, 0];
[00127812:error] !!! []: invalid row: 2
m[-3, 0];
[00127824:error] !!! []: invalid row: -3
m[0, 3];
[00127836:error] !!! []: invalid column: 3
m[0, -4];
[00127850:error] !!! []: invalid column: -4
- ’[]=’(row, col, val)
Set the element at row, col to val, and return val. The index row must verify
0 ≤ row < this
.size.first , or −this
.size.first ≤ row < 0, in which case it is equivalent
to using index row + this
.size.first : it counts “backward”. Similarly for col.
var m = Matrix.new([1, 2], [10, 20])|;
assert
{
(m[0, 0] = -1) == -1; m[0, 0] == -1;
(m[-1, -1] = -2) == -2; m[1, 1] == -2;
};
m[2, 0] = -1;
[00127812:error] !!! []=: invalid row: 2
m[-3, 0] = -1;
[00127824:error] !!! []=: invalid row: -3
m[0, 2] = -1;
[00127836:error] !!! []=: invalid column: 2
m[0, -3] = -1;
[00127850:error] !!! []=: invalid column: -3
- appendRow(vector)
Append vector to this and return this.
var m2x1 = Matrix.new([0], [1]);
m2x1.appendRow(Vector.new(2)) === m2x1;
m2x1 == Matrix.new([0], [1], [2]);
var m2x2 = Matrix.new([0, 1], [10, 11]);
m2x2.appendRow(Vector.new(20, 21)) == m2x2;
m2x2 == Matrix.new([0, 1], [10, 11], [20, 21]);
Sizes must match.
Matrix.new([0], [1]).appendRow(Vector.new(10, 11));
[00017936:error] !!! appendRow: incompatible sizes: 2x1, 2
Matrix.new([0, 1]).appendRow(Vector.new(10));
[00050922:error] !!! appendRow: incompatible sizes: 1x2, 1
- asMatrix
this.
Matrix.asMatrix() === Matrix;
var m = Matrix.new([1], [2]);
m.asMatrix() === m;
- asPrintable
A String that denotes this.
Matrix .asPrintable() == "Matrix([])";
Matrix.new([1, 2], [3, 4]).asPrintable() == "Matrix([[1, 2], [3, 4]])";
- asString
A String that denotes this.
Matrix .asString() == "<>";
Matrix.new([1, 2], [3, 4]).asString() == "<<1, 2>, <3, 4>>";
- asTopLevelPrintable
A String that denotes this.
Matrix .asTopLevelPrintable()
== "Matrix([])";
Matrix.new([1, 2], [3, 4]).asTopLevelPrintable()
== "Matrix([\n [1, 2],\n [3, 4]])";
- column(i)
The ith column as a Vector. See also row.
var m = Matrix.new([1, 2, 3], [4, 5, 6]);
m.column(0) == Vector.new(1, 4); m.column(-3) == m.column(0);
m.column(1) == Vector.new(2, 5); m.column(-2) == m.column(1);
m.column(2) == Vector.new(3, 6); m.column(-1) == m.column(2);
m.column(3);
[00000232:error] !!! column: invalid column: 3
- createIdentity(size)
The unit Matrix of dimensions size.
Matrix.createIdentity(0) == Matrix;
Matrix.createIdentity(3) == Matrix.new([1, 0, 0], [0, 1, 0], [0, 0, 1]);
Matrix.createIdentity(-2);
[00000328:error] !!! createIdentity: argument 1: expected non-negative integer: -2
- createOnes(row, col)
Same as createScalar(row, col, 1).
Matrix.createOnes(0, 0) == Matrix;
Matrix.createOnes(2, 3) == Matrix.new([1, 1, 1], [1, 1, 1]);
Matrix.createOnes(-2, 2);
[00000328:error] !!! createOnes: argument 1: expected non-negative integer: -2
- createScalars(row, col, scalar)
A Matrix of size row * col filled with scalar.
Matrix.createScalars(0, 0, 99) == Matrix;
Matrix.createScalars(2, 3, 99) == Matrix.new([99, 99, 99], [99, 99, 99]);
Matrix.createScalars(-2, 2, 99);
[00000328:error] !!! createScalars: argument 1: expected non-negative integer: -2
- createZeros(row, col)
Same as createScalar(row, col, 0).
Matrix.createZeros(0, 0) == Matrix;
Matrix.createZeros(2, 3) == Matrix.new([0, 0, 0], [0, 0, 0]);
Matrix.createZeros(-2, 2);
[00000328:error] !!! createZeros: argument 1: expected non-negative integer: -2
- distanceMatrix(that)
Considering that this and that are collections of Vectors that denote positions in an Euclidean
space, produce a Matrix whose value at (i, j) is the distance between points this[i] and
that[j].
// Left-hand side matrix.
var l0 = Vector.new([0, 0]); var l1 = Vector.new([2, 1]);
var lhs = Matrix.new([l0, l1]);
// Right-hand side matrix.
var r0 = Vector.new([0, 1]); var r1 = Vector.new([2, 0]);
var rhs = Matrix.new([r0, r1]);
lhs.distanceMatrix(rhs)
== Matrix.new([l0.distance(r0), l0.distance(r1)],
[l1.distance(r0), l1.distance(r1)]);
- init
See Section 21.38.2.
- inverse
The inverse of this if it exists, raise an error otherwise.
var m = Matrix.new(
[1, 3, 1],
[1, 1, 2],
[2, 3, 4]);
m * m.inverse() == Matrix.createIdentity(3);
m.inverse() * m == Matrix.createIdentity(3);
m.inverse() == Matrix.new(
[ 2, 9, -5],
[ 0, -2, 1],
[-1, -3, 2]);
Matrix.createZeros(2, 2).inverse();
[00000534:error] !!! inverse: non-invertible matrix: <<0, 0>, <0, 0>>
- resize(row, col)
Change the dimensions of this, using 0 for new members. Return this.
// Check that <<1, 2><3, 4>> is equal to the Matrix composed of rows,
// when resized to the dimensions of rows.
function resized(var rows[])
{
var m = Matrix.new([1, 2], [3, 4]);
var res = Matrix.new(rows);
// Resize returns this...
m.resize(res.size.first, res.size.second) === m;
// ...and does resize.
m == res;
}|;
assert
{
// Fewer rows/cols.
resized([1], [3]);
resized([1, 2]);
resized([1]);
resized([]);
// As many rows and cols.
resized([1, 2], [3, 4]);
// More rows/cols.
resized([1, 2, 0], [3, 4, 0]);
resized([1, 2], [3, 4], [0, 0]);
resized([1, 2, 0], [3, 4, 0], [0, 0, 0]);
// More rows, less cols, and conversely.
resized([1], [3], [0]);
resized([1, 2, 0]);
};
- row(i)
The ith row as a Vector. See also column.
var m = Matrix.new([1, 2, 3], [4, 5, 6]);
m.row(0) == Vector.new(1, 2, 3); m.row(-2) == m.row(0);
m.row(1) == Vector.new(4, 5, 6); m.row(-1) == m.row(1);
m.row(2);
[00195645:error] !!! row: invalid row: 2
- rowAdd(vector)
A Matrix whose rows (vectors) are the sum of each row of this with vector.
do (Matrix)
{
assert
{
rowAdd(Vector) == Matrix;
new([[1, 2]]).rowAdd(Vector.new(10)) == new([[11, 12]]);
new([1], [2]).rowAdd(Vector.new(10, 20)) == new([11], [22]);
new([1, 2], [3, 4]).rowAdd(Vector.new(10, 20)) == new([11, 12], [23, 24]);
}
}|;
The dimensions must be compatible: size.first == vector.size.
Matrix.new([1], [2]).rowAdd(Vector.new(10));
[00000415:error] !!! rowAdd: incompatible sizes: 2x1, 1
Matrix.new([1, 2]).rowAdd(Vector.new(10, 20));
[00000425:error] !!! rowAdd: incompatible sizes: 1x2, 2
- rowDiv(vector)
A Matrix whose rows (vectors) are the member-wise division of each row of this with vector.
do (Matrix)
{
assert
{
rowDiv(Vector) == Matrix;
new([[10, 20]]).rowDiv(Vector.new(10)) == new([[1, 2]]);
new([10], [20]).rowDiv(Vector.new(10, 20)) == new([1], [1]);
new([10, 30], [20, 40]).rowDiv(Vector.new(10, 20)) == new([1, 3], [1, 2]);
}
}|;
The dimensions must be compatible: size.first == vector.size.
Matrix.new([1], [2]).rowDiv(Vector.new(10));
[00000415:error] !!! rowDiv: incompatible sizes: 2x1, 1
Matrix.new([1, 2]).rowDiv(Vector.new(10, 20));
[00000425:error] !!! rowDiv: incompatible sizes: 1x2, 2
- rowMul(vector)
A Matrix whose rows (vectors) are the member-wise product of each row of this with vector.
do (Matrix)
{
assert
{
rowMul(Vector) == Matrix;
new([[10, 20]]).rowMul(Vector.new(10)) == new([[100, 200]]);
new([10], [20]).rowMul(Vector.new(10, 20)) == new([100], [400]);
new([1, 2], [3, 4]).rowMul(Vector.new(10, 20)) == new([10, 20], [60, 80]);
}
}|;
The dimensions must be compatible: size.first == vector.size.
Matrix.new([1], [2]).rowMul(Vector.new(10));
[00000415:error] !!! rowMul: incompatible sizes: 2x1, 1
Matrix.new([1, 2]).rowMul(Vector.new(10, 20));
[00000425:error] !!! rowMul: incompatible sizes: 1x2, 2
- rowNorm
A Vector whose values are the (Euclidean) norms of the rows of this.
var m = Matrix.new([1, 2], [3, 3]);
m.rowNorm()[ 0] == m.row( 0).norm();
m.rowNorm()[-1] == m.row(-1).norm();
- rowSub(vector)
A Matrix whose rows (vectors) are the difference of each row of this with vector.
do (Matrix)
{
assert
{
rowSub(Vector) == Matrix;
new([[10, 20]]) .rowSub(Vector.new(1)) == new([[9, 19]]);
new([11], [22]) .rowSub(Vector.new(10, 20)) == new([1], [2]);
new([1, 2], [3, 4]).rowSub(Vector.new(1, 2)) == new([0, 1], [1, 2]);
}
}|;
The dimensions must be compatible: size.first == vector.size.
Matrix.new([1], [2]).rowSub(Vector.new(10));
[00000415:error] !!! rowSub: incompatible sizes: 2x1, 1
Matrix.new([1, 2]).rowSub(Vector.new(10, 20));
[00000425:error] !!! rowSub: incompatible sizes: 1x2, 2
- set(vectors)
Change this to be equal to the Matrix defined by the list of vectors given as argument, and
return this.
var m = Matrix.new([]);
var m1 = Matrix.new([0, 1], [0, 1]);
m === m.set([[0, 1], [0, 1]]);
m == m1;
var m2 = Matrix.new([2, 3]);
m === m.set([[2, 3]]);
m == m2;
- setRow(index, vector)
Set the indexth row of this to vector and return this.
var m2x1 = Matrix.new([0], [1]);
m2x1.setRow(0, Vector.new(2)) === m2x1;
m2x1 == Matrix.new([2], [1]);
var m2x2 = Matrix.new([0, 1], [10, 11]);
m2x2.setRow(0, Vector.new(20, 21)) == m2x2;
m2x2 == Matrix.new([20, 21], [10, 11]);
Sizes and index must match.
Matrix.new([0], [1]).setRow(0, Vector.new(10, 11));
[00017936:error] !!! setRow: incompatible sizes: 2x1, 2
Matrix.new([0, 1]).setRow(0, Vector.new(10));
[00050922:error] !!! setRow: incompatible sizes: 1x2, 1
Matrix.new([0], [1]).setRow(2, Vector.new(10));
[00017936:error] !!! setRow: invalid row: 2
- size(arg)
The dimensions of the this, as a Pair of Floats.
Matrix.size == Pair.new(0, 0);
Matrix.new([1, 2], [3, 4], [5, 6]).size == Pair.new(3, 2);
Matrix.new([1, 2, 3], [4, 5, 6]) .size == Pair.new(2, 3);
- transpose(arg)
The transposed of this.
Matrix .transpose() == Matrix;
Matrix.new([1]) .transpose() == Matrix.new([1]);
Matrix.new([1], [2]) .transpose() == Matrix.new([1, 2]);
Matrix.new([1, 2], [3, 4]).transpose() == Matrix.new([1, 3], [2, 4]);
- type
The String Matrix.
Matrix.type == "Matrix";
Matrix.new([]).type == "Matrix";
21.39 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.
Mutexes must be used when manipulating data structures in a non atomic way to avoid
inconsistent states.
Consider this apparently simple code:
function appendAndTellIfFirst(list, val)
{
var res = list.empty;
list << val;
res
}|;
var l = [];
[00000001] []
appendAndTellIfFirst(l, 1);
[00000002] true
appendAndTellIfFirst(l, 2);
[00000002] false
Now look what happens if called twice in parallel:
l = [];
[00000001] []
var res1; var res2;
res1 = appendAndTellIfFirst(l, 1) & res2 = appendAndTellIfFirst(l, 2)|;
res1;
[00000002] true
res2;
[00000003] true
l.sort(); // order is unspecified
[00000004] [1, 2]
Both tasks checked if the list was empty at the same time, and then appened the element.
A mutex will solve this problem:
l = [];
[00000001] []
var m = Mutex.new();
[00000000] Mutex_0x964ed40
// redefine the function using the mutex
appendAndTellIfFirst = function (list, val)
{m:{
var res = list.empty;
list << val;
res
}}|;
// check again
res1 = appendAndTellIfFirst(l, 1) & res2 = appendAndTellIfFirst(l, 2)|;
// we do not know which one was first, but only one was
[res1, res2].sort();
[00000001] [false, true]
l.sort();
[00000004] [1, 2]
Mutex constructor accepts an optional maximum queue size: code blocks trying to wait when
maximum queue size is reached will not be executed:
var m = Mutex.new(1)|;
var e = Event.new()|;
at(e?)
m: { echo("executing at"); sleep(200ms);};
e!;e!;e!;
sleep(600ms);
[00000001] *** executing at
[00000001] *** executing at
As you can see above the message is only displayed twice: First at got executed right away,
the second was queued and executed when the first one finished, and the third one got
stopped.
- asMutex
Return this.
var m1 = Mutex.new()|;
assert
{
m1.asMutex() === m1;
};
21.40 nil
The special entity nil is an object used to denote an empty value. Contrary to 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; !42.isNil; !(function () { nil }.isNil);
- isVoid
In order to facilitate the transition from older code to newer, return true. In the future, false will
be returned. Therefore, if you really need to check whether foo is void but not nil, use
!foo.acceptVoid().isNil && foo.isVoid.
nil.isVoid;
[ Logger ] nil.isVoid will return false eventually, adjust your code.
[ Logger ] For instance replace InputStream loops from
[ Logger ] while (!(x = i.get().acceptVoid()).isVoid())
[ Logger ] cout << x;
[ Logger ] to
[ Logger ] while (!(x = i.get()).isNil())
[ Logger ] cout << x;
21.41 Object
Object includes the mandatory primitives for all objects in urbiscript. All objects in urbiscript
must inherit (directly or indirectly) from it.
A fresh object can be instantiated by cloning Object itself.
Object.new();
[00000421] Object_0x00000000
The keyword class also allows to define objects intended to serve as prototype of a family
of objects, similarly to classes in traditional object-oriented programming languages (see
Section 7.4).
{
class Foo
{
var attr = 23;
};
assert
{
Foo.localSlotNames() == ["asFoo", "attr", "type"];
Foo.asFoo() === Foo;
Foo.attr == 23;
Foo.type == "Foo";
};
};
- ’!’
Logical negation. If this evaluates to false return true and vice-versa.
!1 == false;
!0 == true;
!"foo" == false;
!"" == true;
- ’!==’(that)
The negation of \this === \that, see ’===’.
var o1 = Object.new();
var o2 = Object.new();
o1 !== o2;
!(o1 !== o1);
1 !== 1;
"1" !== "1";
[1] !== [1];
- ’&&’(that)
Short-circuiting logical (Boolean) and. If this evaluates to true evaluate and return that,
otherwise return this without evaluating that.
(0 && "foo") == 0;
(2 && "foo") == "foo";
("" && "foo") == "";
("foo" && "bar") == "bar";
- ’*=’(that)
Bounce to this * that (Section 20.1.8.2).
var x = 2;
(x *= 5) == 10; x == 10;
x.’*=’(2) == 20; x == 10;
- ’+=’(that)
Bounce to this + that. Be sure to understand how in-place operators are handled in urbiscript:
Section 20.1.8.2.
var x = 1;
(x += 1) == 2; x == 2;
x.’+=’(1) == 3; x == 2;
- ’-=’(that)
Bounce to this - that (Section 20.1.8.2).
var x = 10;
(x -= 3) == 7; x == 7;
x.’-=’(3) == 4; x == 7;
- ’/=’(that)
Bounce to this / that (Section 20.1.8.2).
var x = 200;
(x /= 10) == 20; x == 20;
x.’/=’(2) == 10; x == 20;
- ’==’(that)
Whether this and that are equal. See also Comparable and Section 20.1.8.6. By
default, bounces to ’===’. This operator must be redefined for objects that have a
value-semantics; for instance two String objects that denotes the same string should be
equal according to ==, although physically different (i.e., not equal according to ===).
var o1 = Object.new();
var o2 = Object.new();
o1 == o1;
!(o1 == o2);
o1 != o2;
!(o1 != o1);
1 == 1;
"1" == "1";
[1] == [1];
- ’===’(that)
Whether this and that are exactly the same object (i.e., this and that are two different
means to denote the very same location in memory). To denote equivalence, use ’==’;
for instance two Float objects that denote 42 can be different objects (in the sense
of ===), but will be considered equal by ==. See also ’===’ and Section 20.1.8.6.
var o1 = Object.new();
var o2 = Object.new();
o1 === o1;
!(o1 === o2);
!( 1 === 1 );
!("1" === "1");
!([1] === [1]);
- ’^’(that)
Logical exclusive or. If this evaluates to false evaluate and return that, otherwise
return !that. Beware that the semantics of Float.’^’ (bitwise exclusive or, not
logical logical or) is different, so in case of doubt use a.asBool ^ b instead of a ^ b.
"foo" ^ "" === true;
"" ^ "foo" == "foo";
"" ^ 1 == 1;
"" ^ "" == "";
"a" ^ "b" === false;
// Beware of bitwise operations.
1 ^ 2 == 3; // As a Boolean, 3 is "true".
1.asBool ^ 2 === false;
- ’^=’(that)
Bounce to this ^ that (Section 20.1.8.2).
var x = 0xff00;
(x ^= 0xffff) == 0x00ff; x == 0x00ff;
x.’^=’(0xffff) == 0xff00; x == 0x00ff;
- ’||’(that)Short-circuiting logical (Boolean) or. If this evaluates to false evaluate and return
that, otherwise return this without evaluating that.
(0 || "foo") == "foo";
(2 || 1/0) == 2;
("" || "foo") == "foo";
("foo" || 1/0) == "foo";
- ’$id’
- acceptVoid
Return this. See void to know why.
var o = Object.new();
o.acceptVoid() === o;
- addProto(proto)
Add proto into the list of prototypes of this. Return this.
do (Object.new())
{
assert
{
addProto(Orderable) === this;
protos == [Orderable, Object];
};
}|;
- allProto
A list with this, its parents, their parents,…
123.allProtos().size == 6;
- allSlotNames
Deprecated alias for slotNames.
Object.allSlotNames() == Object.slotNames();
- apply(args)
“Invoke this”. The size of the argument list, args, must be one. This argument is ignored. This
function exists for compatibility with Code.apply.
Object.apply([this]) === Object;
Object.apply([1]) === Object;
- as(type)
Convert this to type. This is syntactic sugar for asType when Type is the type of type.
12.as(Float) == 12;
"12".as(Float) == 12;
12.as(String) == "12";
Object.as(Object) === Object;
- asBool
Whether this is “true”, see Section 21.3.1.
Global.asBool() == true;
nil.asBool() == false;
void.asBool();
[00000421:error] !!! unexpected void
- asPrintable
A String that can be used to display faithfully this using Lobby.echo. Defaults to
asString.
For instance, String.asString returns the string itself, but "foo".asPrintable is the string
"\"foo\"", so that using echo, you would see "foo".
Object.asPrintable().isA(String);
Object.asPrintable() == Object.asString();
"foo".asString() == "foo";
"foo".asPrintable() == "\"foo\"";
See also asString and asTopLevelPrintable.
- asString
A conversion of this into a String. In general:
- if this is a value for which there is a literal syntax (e.g., Float, String, List, etc.), then
use that syntax:
3.1415.asString() == "3.1415";
"Hello, World!".asString() == "Hello, World!";
[1, "2", [3]].asString() == "[1, \"2\", [3]]";
- if this a “class”, then the name of the class, otherwise a String composed of the name of
the “class” the object is an instance of, and some integral value that depends on this.
class Class {}|;
Class.asString();
[00002106] "Class"
var c1 = Class.new()|; var c2 = Class.new()|;
c1.asString();
[00002112] "Class_0x1040e0108"
c2.asString();
[00002115] "Class_0x1040c17a8"
assert
{
c1.asString() == c1.asString() != c2.asString() == c2.asString();
c1.asString() == c1.type + "_" + c1.uid();
c2.asString() == c2.type + "_" + c2.uid();
};
See also asTopLevelPrintable and asPrintable.
- asTopLevelPrintable
A String used to display this in interactive sessions, or nil if this should be not be printed.
class Test
{
function init(v) { var this.value = v };
function asTopLevelPrintable() { value };
}|;
Test.new("12");
[00004345] 12
// Nothing to display here.
Test.new(nil);
// This is an error, asTopLevelPrintable must return a string or nil.
Test.new(1);
[00004370:error] !!! send: argument 1: unexpected 1, expected a String
Defaults to asPrintable. For instance, since nil and void are not reported in interactive
sessions, their asTopLevelPrintable is nil, but not their asPrintable.
Object.asTopLevelPrintable().isA(String);
Object.asTopLevelPrintable() == Object.asPrintable();
// A version of void on which we can call methods.
var Void = void.acceptVoid();
nil.asPrintable() == "nil"; nil.asTopLevelPrintable() == nil;
Void.asPrintable() == "void"; Void.asTopLevelPrintable() == nil;
See also asString and asPrintable.
- bounce(name)
Return this.name transformed from a method into a function that takes its target (its “this”)
as first and only argument. this.name must take no argument.
{ var myCos = Object.bounce("cos"); myCos(0) } == 0.cos();
{ var myType = bounce("type"); myType(Object); } == "Object";
{ var myType = bounce("type"); myType(3.14); } == "Float";
- callMessage(msg)
Invoke the CallMessage msg on this.
- clone
Clone this, i.e., create a fresh, empty, object, which sole prototype is this.
Object.clone().protos == [Object];
Object.clone().localSlotNames() == [];
- cloneSlot(from, to)
Set the new slot to using a clone of from. This can only be used into the same object.
var foo = Object.new() |;
cloneSlot("foo", "bar") |;
assert(!(&foo === &bar));
- copySlot(from, to)
Same as cloneSlot, but the slot aren’t cloned, so the two slot are the same.
var moo = Object.new() |;
copySlot("moo", "loo") |;
assert(&moo === &loo);
- createSlot(name)
Create an empty slot (which actually means it is bound to void) named name. Raise an error if
name was already defined.
var o = Object.new();
!o.hasLocalSlot("foo");
o.createSlot("foo").isVoid;
o.hasLocalSlot("foo");
- dump(depth)
Describe this: its prototypes and slots. The argument depth specifies how recursive
the description is: the greater, the more detailed. This method is mostly useful for
debugging low-level issues, for a more human-readable interface, see also inspect.
do (2) { var this.attr = "foo"; this.attr->prop = "bar" }.dump(0);
[00015137] *** Float_0x240550 {
[00015137] *** /* Special slots */
[00015137] *** protos = Float
[00015137] *** value = 2
[00015137] *** /* Slots */
[00015137] *** attr = String_0x23a750 <...>
[00015137] *** /* Properties */
[00015137] *** prop = String_0x23a7a0 <...>
[00015137] *** value = String_0x23a750 <...>
[00015137] *** }
do (2) { var this.attr = "foo"; this.attr->prop = "bar" }.dump(1);
[00020505] *** Float_0x42389f08 {
[00020505] *** /* Special slots */
[00020505] *** protos = Float
[00020505] *** value = 2
[00020505] *** /* Slots */
[00020505] *** attr = String_0x42392b48 {
[00020505] *** /* Special slots */
[00020505] *** protos = String
[00020505] *** /* Slots */
[00020505] *** }
[00020505] *** /* Properties */
[00020505] *** prop = String_0x239330 {
[00020505] *** /* Special slots */
[00020505] *** protos = String
[00020505] *** /* Slots */
[00020505] *** }
[00020505] *** value = String_0x42392b48 {
[00020505] *** /* Special slots */
[00020505] *** protos = String
[00020505] *** /* Slots */
[00020505] *** }
[00020505] *** }
Object.dump(1);
[00020555] *** Object {
[00020555] *** /* Special slots */
[00020555] *** protos = Comparable, Global
[00020555] *** /* Slots */
[...]
[00020555] *** }
[00020555] *** }
0.dump(1.5);
[00020605:error] !!! dump: expected integer: 1.5
- getLocalSlot(name)
The value associated to name in this, excluding its ancestors (contrary to getSlot).
var a = Object.new()|;
// Local slot.
var a.slot = 21|;
assert
{
a.locateSlot("slot") === a;
a.getLocalSlotValue("slot") == 21;
};
// Inherited slot are not looked-up.
assert { a.locateSlot("init") == Object };
a.getLocalSlotValue("init");
[00041066:error] !!! lookup failed: init
- getPeriod
Deprecated. Use System.period instead.
- getProperty(slotName, propName)
The value of the propName property associated to the slot slotName if defined. Raise an error
otherwise.
const var myPi = 3.14|;
assert (getProperty("myPi", "constant"));
getProperty("myPi", "foobar");
[00000045:error] !!! property lookup failed: myPi->foobar
- getSlot(name)
The value associated to name in this, possibly after a look-up in its prototypes (contrary to
getLocalSlot).
var b = Object.new();
var b.slot = 21;
// Local slot.
b.locateSlot("slot") === b;
b.getSlotValue("slot") == 21;
// Inherited slot.
b.locateSlot("init") === Object;
b.getSlotValue("init") == Object.getSlotValue("init");
// Unknown slot.
b.locateSlot("ENOENT") == nil;
b.getSlotValue("ENOENT");
[00041066:error] !!! lookup failed: ENOENT
- hash
A Hash object for this. This default implementation returns a different hash for every object, so
every key maps to a different cells. Classes that have value semantic should override the hash
method so as objects that are equal (in the Object.’==’ sense) have the same hash.
String.hash does so for instance; as a consequence different String objects with the same value
map to the same cell.
A hash only makes sense as long as the hashed object exists.
var o1 = Object.new();
var o2 = Object.new();
o1.hash() == o1.hash();
o1.hash() != o2.hash();
- hasLocalSlot(slot)
Whether this features a slot slot, locally (not from some ancestor). See also hasSlot.
class Base { var this.base = 23; } |;
class Derive: Base { var this.derive = 43 } |;
assert(Derive.hasLocalSlot("derive"));
assert(!Derive.hasLocalSlot("base"));
- hasProperty(slotName, propName)
Whether the slot slotName of this has a property propName.
var o = Object.new();
const var o.halfPi = Math.pi / 2;
o.hasProperty("halfPi", "constant");
!o.hasProperty("halfPi", "foobar");
- hasSlot(slot)
Whether this has the slot slot, locally, or from some ancestor. See also hasLocalSlot.
Derive.hasSlot("derive");
Derive.hasSlot("base");
!Base.hasSlot("derive");
- inspect(deep = false)
Describe this: its prototypes and slots, and their properties. If deep, all the slots are described,
not only the local slots. See also dump.
do (2) { var this.attr = "foo"; this.attr->prop = "bar"}.inspect();
[00001227] *** Inspecting 2
[00001227] *** ** Prototypes:
[00001227] *** 0
[00001227] *** ** Local Slots:
[00001228] *** attr : gettable
[00001228] *** Properties:
[00001228] *** prop : String = "bar"
- isA(obj)
Whether this has obj in his parents.
Float.isA(Orderable);
! String.isA(Float);
- isNil
Whether this is nil.
- isProto
Whether this is a prototype.
Float.isProto;
! 42.isProto;
- isVoid
Whether this is void. See void.
void.isVoid;
! 42.isVoid;
- localSlotNames
A list with the names of the local (i.e., not including those of its ancestors) slots of this. See also
slotNames.
var top = Object.new();
var top.top1 = 1;
var top.top2 = 2;
var bot = top.new();
var bot.bot1 = 10;
var bot.bot2 = 20;
top.localSlotNames() == ["top1", "top2"];
bot.localSlotNames() == ["bot1", "bot2"];
- locateSlot(slot)
The Object that provides slot to this, or nil if this does not feature slot.
locateSlot("locateSlot") == Object;
locateSlot("doesNotExist").isNil;
- print
Send this to the Channel.topLevel channel.
1.print();
[00001228] 1
[1, "12"].print();
[00001228] [1, "12"]
- properties(slotName)
A dictionary of the properties of slot slotName. Raise an error if the slot does not exist.
2.properties("foo");
[00238495:error] !!! lookup failed: foo
do (2) { var foo = "foo" }.properties("foo");
[00238501] ["constant" => false]
do (2) { var foo = "foo" ; foo->bar = "bar" }.properties("foo");
[00238502] ["bar" => "bar", "constant" => false]
- protos
The list of prototypes of this.
- removeLocalSlot(slot)
Remove slot from the (local) list of slots of this, and return this. Raise an error if slot does
not exist. See also removeSlot.
var base = Object.new()|;
var base.slot = "base"|;
var derive = Base.new|;
var derive.slot = "derive"|;
derive.removeLocalSlot("foo");
[00000080:error] !!! lookup failed: foo
assert
{
derive.removeLocalSlot("slot") === derive;
derive.localSlotNames() == [];
base.slot == "base";
};
derive.removeLocalSlot("slot");
[00000090:error] !!! lookup failed: slot
assert
{
base.slot == "base";
};
- removeProperty(slotName, propName)
Remove the property propName from the slot slotName. Raise an error if the slot does
not exist. Warn if propName does not exist; in a future release this will be an error.
var r = Object.new()|;
// Non-existing slot.
r.removeProperty("slot", "property");
[00000072:error] !!! lookup failed: slot
var r.slot = "slot value"|;
// Non-existing property.
r.removeProperty("slot", "property");
[00000081:warning] !!! no such property: slot->property
[00000081:warning] !!! called from: removeProperty
r.slot->property = "property value"|;
assert
{
r.hasProperty("slot", "property");
// Existing property.
r.removeProperty("slot", "property") == "property value";
! r.hasProperty("slot", "property");
};
- removeProto(proto)
Remove proto from the list of prototypes of this, and return this. Do nothing if proto is not a
prototype of this.
var o = Object.new();
o.addProto(Orderable);
o.removeProto(123) === o;
o.protos == [Orderable, Object];
o.removeProto(Orderable) === o;
o.protos == [Object];
- removeSlot(slot)
Remove slot from the (local) list of slots of this, and return this. Warn if slot
does not exist; in a future release this will be an error. See also removeLocalSlot.
var base = Object.new();
var base.slot = "base";
var derive = Base.new();
var derive.slot = "derive";
derive.removeSlot("foo") === derive;
[00000080:warning] !!! no such local slot: foo
[00000080:warning] !!! called from: removeSlot
[00000080:warning] !!! called from: code
[00000080:warning] !!! called from: eval
[00000080:warning] !!! called from: value
[00000080:warning] !!! called from: assertCall
derive.removeSlot("slot") === derive;
derive.localSlotNames() == [];
base.slot == "base";
derive.removeSlot("slot") === derive;
[00000099:warning] !!! no such local slot: slot
[00000099:warning] !!! called from: removeSlot
[00000099:warning] !!! called from: code
[00000099:warning] !!! called from: eval
[00000099:warning] !!! called from: value
[00000099:warning] !!! called from: assertCall
base.slot == "base";
- setConstSlot
Like setSlotValue but the created slot is const.
assert(setConstSlotValue("fortyTwo", 42) == 42);
fortyTwo = 51;
[00000000:error] !!! cannot modify const slot
- setProperty(slotName, propName, value)
Set the property propName of slot slotName to value. Raise an error in slotName does not
exist. Return value. This is what slotName->propName = value actually performs.
var o = Object.new();
var o.slot = "slot";
var value = "value";
o.setProperty("slot", "prop", value) === value;
"prop" in o.properties("slot");
o.getProperty("slot", "prop") === value;
o.slot->prop === value;
o.setProperty("slot", "noSuchProperty", value) === value;
o.setProperty("noSuchSlot", "prop", "12");
[00000081:error] !!! lookup failed: noSuchSlot
In order to respect copy-on-write semantics, when a property is added to an inherited slot, the
slot value is first copied.
var top = Object.new();
var top.x = "top";
var bot = top.new();
// bot.x is inherited from top.
bot.locateSlot("x") === top;
// Setting the property from bot’s point of view detaches bot.x from
// top.x.
bot.x->prop = "irrelevant";
bot.locateSlot("x") === bot;
bot.x == top.x;
bot.x === top.x;
// top.x and bot.x are detached.
top.x = "new top";
bot.x == "top";
- setProtos(protos)
Set the list of prototypes of this to protos. Return void.
var o = Object.new();
o.protos == [Object];
o.setProtos([Orderable, Object]).isVoid;
o.protos == [Orderable, Object];
- setSlot(name, value)
Create a slot name mapping to value. Raise an error if name was already defined. This is what
var name = value actually performs.
Object.setSlot("theObject", Object) === Object;
Object.theObject === Object;
theObject === Object;
If the current job is in redefinition mode, setSlot on an already defined slot is not an error and
overwrites the slot like updateSlot would. See System.redefinitionMode.
- slotNames
A list with the slot names of this and its ancestors.
Object.localSlotNames()
.subset(Object.slotNames());
Object.protos.foldl(function (var r, var p) { r + p.localSlotNames() },
[])
.subset(Object.slotNames());
- type
The name of the type of this. The class construct defines this slot to the name of the class
(Section 7.4). This is used to display the name of “instances”.
class Example {};
[00000081] Example
assert
{
Example.type == "Example";
};
Example.new();
[00000081] Example_0x423740c8
- uid
The unique id of this.
var foo = Object.new();
var bar = Object.new();
foo.uid == foo.uid;
foo.uid != bar.uid;
- unacceptVoid
Return this. See void to know why.
var o = Object.new();
o.unacceptVoid() === o;
- updateSlot(name, value)
Map the existing slot named name to value. Raise an error if name was not defined.
Object.setSlot("one", 1) == 1;
Object.updateSlot("one", 2) == 2;
Object.one == 2;
Implements copy-on-write, i.e., when updating an inherited slot, first copy this slot in
this.
var top = Object.new();
var top.x = 123;
var bot = top.new();
// x is inherited.
bot.locateSlot("x") === top;
bot.updateSlot("x", 456) == 456;
// x is local.
bot.locateSlot("x") === bot;
If the slot is declared constant (Section 20.4.2), updating the slot is invalid, but copy-on-write
will work.
class Top
{
const var x = [1];
}|;
// Cannot update local const slot.
Top.updateSlot("x", [2]);
[00007858:error] !!! cannot modify const slot
// Updating inherited const slot is possible.
var bot = Top.new()|;
bot.updateSlot("x", [3]);
[00007872] [3]
The constant property prevents the slot from being assigned a new value, it does not forbid
mutable values from being modified.
Top.x << 2|;
assert
{
// The associated value is updated.
Top.x == [1, 2];
};
21.42 Orderable
Objects that have a concept of “less than”. See also 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 ’<’ (that) { value < that.value; };
function asString() { "<" + value.asString + ">"; };
}|;
var one = Foo.new(1)|;
var two = Foo.new(2)|;
assert
{
one <= one ; one <= two ; !(two <= one);
!(one > one) ; !(one > two) ; two > one;
(one >= one) ; !(one >= two) ; two >= one;
};
- ’<’(that)
Whether this <= that && this != that.
- ’<=’(that)
Whether that > this || this == that.
- ’>’(that)
Whether this >= that && this != that.
- ’>=’(that)
Whether that < this || this != that.
21.43 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. If the file already
exists, content is appended to it. Remove the file beforehand if you want to override its
content.
var o1 = OutputStream.new(File.create("file.txt"));
[00001379] OutputStream_0x10808a300
var o2 = OutputStream.new(File.new("file.txt"));
[00001396] OutputStream_0x1080872e0
Cloning a closed Stream is valid, but it is forbidden to clone an opened one.
var o3 = OutputStream.clone().new(File.new("file.txt"));
[00001399] OutputStream_0x10803e7a0
o1.clone();
[00001401:error] !!! clone: cannot clone opened Streams
Do not forget to close the streams you opened (Section 21.64.2).
o1.close();
o2.close();
o3.close();
- ’<<’(that)
Output this.asString. Return this to enable chains of calls. Raise an error if the file is closed.
var o = OutputStream.new(File.create("fresh.txt"))|;
o << 1 << "2" << [3, [4]]|;
o.close();
assert (File.new("fresh.txt").content.data == "12[3, [4]]");
o << 1;
[00000005:error] !!! <<: stream is closed
- flush
To provide 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. Raise an error if the file is closed.
var s = OutputStream.new(File.create("file.txt"))|
s.flush();
s.close();
s.flush();
[00039175:error] !!! flush: stream is closed
- put(byte)
Output the character corresponding to the numeric code byte in this, and return this. Raise an
error if the file is closed.
var f = File.create("put.txt") |
var os = OutputStream.new(f) |
assert
{
os.put(0)
.put(255)
.put(72).put(101).put(108).put(108).put(111)
=== os;
f.content.data == "\0\xffHello";
};
os.put(12.5);
[00029816:error] !!! put: argument 1: expected integer: 12.5
os.put(-1);
[00034840:error] !!! put: argument 1: expected non-negative integer: -1
os.put(256);
[00039175:error] !!! put: argument 1: expected non-positive integer: 256
os.close();
os.put(0);
[00039179:error] !!! put: stream is closed
21.44 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] !!! new: expected 2 arguments, given 0
Pair.new(1, 2, 3, 4);
[00000003:error] !!! new: 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;
21.45 Path
A Path points to a file system entity (directory, file and so forth).
Path itself is the root of the file system: / on Unix, and C:\ on Windows.
Path;
[00000001] Path("/")
A Path is constructed with the string that points to the file system entity. This path can be relative
or absolute.
Path.new("foo");
[00000002] Path("foo")
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")
- ’/’(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.
Path.new("/a") < Path.new("/a/b");
!(Path.new("/a/b") < Path.new("/a") );
- ’==’(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") );
- 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. See also dirname.
Path.new("/absolute/path/file.u").basename == "file.u";
Path.new("relative/path/file.u").basename == "file.u";
- cd
Change the current working directory to this. Return the new current working directory as a
Path.
var a = Directory.create("a").asPath();
var b = Directory.create("a/b").asPath();
var cwd = Path.cwd; // Current location.
cwd.isA(Path);
// cd returns the new current working directory.
b.cd() == cwd / b == cwd / "a" / "b";
Path.cwd == cwd / b;
// Go back to the original location.
Path.new("../..").cd() == cwd;
Path.cwd == cwd;
Exceptions are thrown on cases of error.
Path.new("does/not/exist").cd();
[00003991:error] !!! cd: no such file or directory: does/not/exist
var f = File.create("file.txt")|;
f.asPath().cd();
[00099415:error] !!! cd: not a directory: file.txt
Permissions are not properly handled on Windows, so the following example would actually fail.
var d = Directory.create("forbidden")|;
System.system("chmod 444 %s" % d)|;
d.asPath().cd();
[00140753:error] !!! cd: Permission denied: forbidden
- cwd
The current working directory.
// Save current directory.
var pwd = Path.cwd;
// Go into "/".
var root = Path.new("/").cd();
// Current working directory is "/".
Path.cwd == root;
// Go back to the directory we were in.
pwd.cd() == pwd;
- dirname
Directory name of the path. See also basename.
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;
var p = Path.new("file.txt");
!p.exists;
File.create(p);
p.exists;
- isDir
Whether this is a directory.
Path.cwd.isDir;
var f = File.create("file.txt");
!f.asPath().isDir;
!Path.new("does/not/exist").isDir;
- isReg
Whether this is a regular file.
var f = File.create("file.txt");
f.asPath().isReg;
!Path.cwd.isReg;
!Path.new("does/not/exist").isReg;
- lastModifiedDate
Last modified date of the path.
var p = Path.new("test");
File.create(p);
0 <= Date.now - p.lastModifiedDate <= 5s;
- open
Open this. Return either a Directory or a File according the type of this. See File and
Directory.
Path.new("/").open().isA(Directory);
- readable
Whether this is readable. Throw if does not even exist. On Windows, always returns true.
Path.new(".").readable;
var p = Path.new("file.txt");
File.create(p);
p.readable;
System.system("chmod a-r %s" % p) == 0;
!p.readable;
System.system("chmod a+r %s" % p) == 0;
p.readable;
- rename(name)
Rename the file-system object (directory, file, etc.) pointed to by this, as name. Return void.
var dir1 = Directory.create("dir1");
var p = dir1.asPath();
p.rename("dir2").isVoid;
p.basename == "dir2";
- writable
Whether this is writable. Throw if does not even exist. On Windows, always returns true.
Path.new(".").writable;
var p = Path.new("file.txt");
File.create(p);
p.writable;
System.system("chmod a-w %s" % p) == 0;
!p.writable;
System.system("chmod a+w %s" % p) == 0;
p.writable;
21.46 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]); // create a pattern to match the list [1].
[00000000] Pattern_0x189ea80
Pattern.new(Pattern.Binding.new("a")); // match anything into "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.
- 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
};
- 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);
21.47 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)
A new Position shifted from n columns to the right of this. 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)
A new Position shifted from n columns to the left of this. 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);
- ’<’(that)
Order comparison of lines and columns.
Position.new(2, 3) < Position.new(2, 4);
Position.new(2, 3) < Position.new(3, 1);
- ’==’(that)
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);
- asString
Present as ‘file:line.column ’, the file name is omitted if it is not defined.
Position.new("file.u", 2, 3).asString() == "file.u:2.3";
- column
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
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);
21.48 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.getSlotValue("+").apply([1, 2]) == 3;
String.getSlotValue("+").apply(["1", "2"]);
- asPrimitive
Return this.
Float.getSlotValue("+").asPrimitive() === Float.getSlotValue("+");
21.49 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 (the output of the Process is an input for Urbi) to the standard error stream of
the process.
do (Process.new("urbi-send", ["--no-such-option"]))
{
run();
join();
assert
{
stderr.asList() ==
["urbi-send: invalid option: --no-such-option",
"Try ‘urbi-send --help’ for more information."];
};
}|;
- stdin
An 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-2012 Gostai S.A.S.";
};
}|;
- stdout
An 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!"]);
}|;
21.50 Profile
A Profile object contains information about the efficiency of a piece of code.
One can profile a piece of code with the System.profile function.
var profile = System.profile(function() { echo("foo") });
[00000001] *** foo
[00001672] Profile(
Yields: 0
Total time (us): 1085
Wall-clock time (us): 1085
Function calls: 12
Max depth: 5
.-------------------------------------------------------------------.
| function | % | cumulative | self | calls | self |
| | | (us) | (us) | | (us/call) |
|--------------+--------+------------+--------+---------+-----------|
| apply | 26.91 | 292 | 292 | 1 | 292 |
| echo | 25.35 | 567 | 275 | 1 | 275 |
| <profiled> | 20.18 | 786 | 219 | 1 | 219 |
| send | 6.36 | 855 | 69 | 1 | 69 |
| apply | 4.61 | 905 | 50 | 1 | 50 |
| oget | 4.24 | 951 | 46 | 2 | 23 |
| + | 4.06 | 995 | 44 | 2 | 22 |
| getSlotValue | 3.32 | 1031 | 36 | 1 | 36 |
| + | 2.76 | 1061 | 30 | 1 | 30 |
| asString | 2.21 | 1085 | 24 | 1 | 24 |
’--------------’--------’------------’--------’---------’-----------’
)
The result is a Profile object that contains information about which functions where used when
evaluating the given code, how many time they were called, how much time was spent in them, …Lines
are sorted by decreasing “self time”. Note that the <profiled> special function stands for the function
given in parameter. Every line is represented by a Profile.Function object, see its documentation for
the meaning of every column.
If the profiled code spawns asynchronous tasks via detach or at for instance, additional statistics will
be included in the resulting Profile every time the detached code is executed. This is extremely useful
to profile asynchronous code based on at for instance.
var x = false|;
// Make sure ’x’ is visible whoever the caller of ’profiled’ is.
import this.*;
function profiled()
{
at (x)
echo("true")
onleave
echo("false")
}|;
// This is the profiling for the creation of the ’at’. Note that the
// condition was already evaluated once, to see whether it should trigger
// immediately.
var profile_async = System.profile(getSlotValue("profiled"));
[00000000] Profile(
Yields: 0
Total time (us): 73
Wall-clock time (us): 73
Function calls: 9
Max depth: 4
.-------------------------------------------------------------------.
| function | % | cumulative | self | calls | self |
| | | (us) | (us) | | (us/call) |
|--------------+--------+------------+--------+---------+-----------|
| <profiled> | 46.58 | 34 | 34 | 1 | 34 |
| at: { x } | 21.92 | 50 | 16 | 1 | 16 |
| onEvent | 15.07 | 61 | 11 | 1 | 11 |
| clone | 9.59 | 68 | 7 | 2 | 3.500 |
| new | 2.74 | 72 | 2 | 2 | 1 |
| init | 1.37 | 73 | 1 | 2 | 0.500 |
’--------------’--------’------------’--------’---------’-----------’
)
// Trigger the at twice.
x = true|;
[00106213] *** true
x = false|;
[00172119] *** false
// The profile now includes additional statistic about the evaluations of
// the condition and the bodies of the at.
profile_async;
[00178623] Profile(
Yields: 2
Total time (us): 251
Wall-clock time (us): 251
Function calls: 29
Max depth: 4
.-------------------------------------------------------------------.
| function | % | cumulative | self | calls | self |
| | | (us) | (us) | | (us/call) |
|--------------+--------+------------+--------+---------+-----------|
| <profiled> | 13.55 | 34 | 34 | 1 | 34 |
| event | 11.55 | 63 | 29 | 1 | 29 |
| send | 11.16 | 91 | 28 | 2 | 14 |
| event | 10.76 | 118 | 27 | 1 | 27 |
| at: { x } | 10.76 | 145 | 27 | 3 | 9 |
| clone | 5.98 | 183 | 15 | 4 | 3.750 |
| echo | 5.58 | 212 | 14 | 2 | 7 |
| onEvent | 4.38 | 223 | 11 | 1 | 11 |
| + | 1.99 | 228 | 5 | 4 | 1.250 |
| new | 1.99 | 233 | 5 | 4 | 1.250 |
| asString | 0.80 | 242 | 2 | 2 | 1 |
| init | 0.80 | 248 | 2 | 4 | 0.500 |
’--------------’--------’------------’--------’---------’-----------’
)
Note that part of the internal machinery shows in these figures (and left visible on purpose).
For instance the three additional calls to new correspond to the creation of the changed
event.
Profile objects are not meant to be cloned as they are created by System.profile internal
machinery.
- calls
Return a List of Profile.Function objects. Each element of this list describes, for a
given function, statistics about how many times it is called and how much time is spent
in it.
- Function
See Profile.Function.
- maxFunctionCallDepth
The maximum function call depth reached.
- totalCalls
The total number of function calls made.
- totalTime
The total CPU time. It can be higher than the wall clock time on multi-core processors
for instance.
- wallClockTime
The time spent between the beginning and the end as if measured on a wall clock.
- yields
The scheduler has to execute many coroutines in parallel. A coroutine yields when it gives
the opportunity to an other to be executed until this one yields and so on…This slot contains
the number of scheduler yields.
21.51 Profile.Function
A Function object contains information about calls of a given function during a profiling
operation.
Function objects are not meant to be cloned as they are created by System.profile internal
machinery. As an example, let us profile the traditional factorial function.
function Float.fact()
{
if (this <= 1)
this
else
this * (this - 1).fact();
}|;
To improve the consistency of the results, you are advised to run the profiling system (and function
to profile) once before the real measure. This ensures that all the code is loaded and ready to be run:
profiling will be about computations, not about initializations.
System.profile(function() { 20.fact() })|;
var profile = System.profile(function() { 20.fact() });
[00009050] Profile(
Yields: 0
Total time (us): 171
Wall-clock time (us): 171
Function calls: 79
Max depth: 22
.-------------------------------------------------------------------.
| function | % | cumulative | self | calls | self |
| | | (us) | (us) | | (us/call) |
|--------------+--------+------------+--------+---------+-----------|
| fact | 70.18 | 120 | 120 | 20 | 6 |
| - | 10.53 | 138 | 18 | 19 | 0.947 |
| <= | 8.77 | 153 | 15 | 20 | 0.750 |
| * | 8.19 | 167 | 14 | 19 | 0.737 |
| <profiled> | 2.34 | 171 | 4 | 1 | 4 |
’--------------’--------’------------’--------’---------’-----------’
)
profile.calls[0];
[00123833] Function(’fact’, 20, 0.000120, 0.000006)
profile.calls[0].isA(Profile.Function);
[00123933] true
- calls
The number of times this function was called during the profiling.
// Example continued from Construction section.
profile.calls[0].calls == 20;
- name
The name of the function called.
// Example continued from Construction section.
profile.calls[0].name == "fact";
- selfTime
Total CPU time spent in all calls of the function.
// Example continued from Construction section.
profile.calls[0].selfTime.isA(Float);
- selfTimePer
Average CPU time spent in one function call. It is computed as the ratio of selfTime divided by
calls.
// Example continued from Construction section.
do (profile.calls[0])
{
selfTimePer == selfTime / calls;
}
21.52 PseudoLazy
21.53 PubSub
PubSub provides an abstraction over Barrier 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.
- 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] []
21.54 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
- 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]
- 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
21.55 RangeIterable
This object is meant to be used as a prototype for objects that support an asList method, to use
range-based for loops (Listing 20.7.6).
- all(fun)
Return whether all the members of the target verify the predicate fun.
// Are all elements positive?
! [-2, 0, 2, 4].all(function (e) { e > 0 });
// Are all elements even?
[-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.
// Is there any even element?
! [-3, 1, -1].any(function (e) { e % 2 == 0 });
// Is there any positive element?
[-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]);
};
21.56 Regexp
A Regexp is an object which allow you to match strings with a regular expression.
A Regexp is created from a regular expression once and for all; it can be used several times to match
with other strings.
Regexp.new(".");
[00000001] Regexp(".")
var num = Regexp.new("\\d+\\.\\d+");
[00000004] Regexp("\\d+\\.\\d+")
"1.3" in num;
[00019618] true
"1." in num;
[00023113] false
urbiscript supports Perl regular expressions, see the perlre man page.
Expressions cannot be empty, and must be syntactically correct.
Regexp.new("");
[00000001:error] !!! new: invalid regular expression: empty expression: ‘’
Regexp.new("(");
[00003237:error] !!! new: invalid regular expression:\
unmatched marking parenthesis ( or \(: ‘(>>>HERE>>>’
Regexp.new("*");
[00004372:error] !!! new: invalid regular expression:\
the repeat operator "*" cannot start a regular expression: ‘>>>HERE>>>*’
- ’[]’(n)
Same as this.matches[n].
var d = Regexp.new("(1+)(2+)(3+)")|;
assert
{
"01223334" in d;
d[0] == "122333";
d[1] == "1";
d[2] == "22";
d[3] == "333";
};
d[4];
[00000009:error] !!! []: out of bound index: 4
- 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 20.1.8.7).
"23.03" in Regexp.new("^\\d+\\.\\d+$");
"-3.14" not in Regexp.new("^\\d+\\.\\d+$");
- match(str)
Whether this matches str.
// Ordinary characters
var r = Regexp.new("oo")|
assert
{
r.match("oo");
r.match("foobar");
!r.match("bazquux");
};
// ^, anchoring at the beginning of line.
r = Regexp.new("^oo")|
assert
{
r.match("oops");
!r.match("woot");
};
// $, anchoring at the end of line.
r = Regexp.new("oo$")|
assert
{
r.match("foo");
!r.match("mooh");
};
// *, greedy repetition, 0 or more.
r = Regexp.new("fo*bar")|
assert
{
r.match("fbar");
r.match("fooooobar");
!r.match("far");
};
// (), grouping.
r = Regexp.new("f(oo)*bar")|
assert
{
r.match("foooobar");
!r.match("fooobar");
};
- matches
If the latest match was successful, the matched groups, as delimited by parentheses in the regular
expression; the first element being the whole match. Otherwise, the empty list. See also
’[]’.
var re = Regexp.new("([a-zA-Z0-9._]+)@([a-zA-Z0-9._]+)")|;
assert
{
re.match("Someone <someone@somewhere.com>");
re.matches == ["someone@somewhere.com", "someone", "somewhere.com"];
"does not match" not in re;
re.matches == [];
};
21.57 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
- 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.
- 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);
// Illustrate that processes can be intertwined
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
- 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.
21.58 Serializables
This object is used to store the set of prototypes that support exchange data between C++ and
urbiscript. See also UValueSerializable and Section 25.18.2.
Nothing specific. Slots should be mapping from “class” name to “class” implementation. See
Section 25.18.2.
21.59 Server
A Server can listen to incoming connections. See 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 as the server.
at (s.connection?(var socket))
{
// This code is run at each connection. ’socket’ is the incoming
// connection.
};
- getIoService
Return the 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.
21.60 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:
// Defining a new class and specifying Singleton as a parent.
class NewSingleton1: Singleton
{
var asString = "NewSingleton1";
}|
var s1 = NewSingleton1.new();
[00000001] NewSingleton1
assert(s1 === NewSingleton1);
assert(NewSingleton1 !== Singleton);
// Create a new Object and set its prototype by hand.
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.
21.61 Slot
A slot is an intermediate object that embodies the concept of “variable” or “field” in urbiscript. It
contains an underlying value, meta-information about this value, and slots to alter the behavior of read
and write operations.
Section Section 20.4.1 describes how to access a slot object.
The contained value returned by default when Object.getSlotValue is called is stored in the value
slot.
The changed slot is an Event that is triggered each time the slot is written to.
Setters and getters to modify the slot behaviors can be installed by writing to set, get, oset and
oget.
Two slots can be linked together to build dataflows using operator >>
It can sometimes be convenient to store two values in one slot, one which is read, and the other
written. For instance, the val slot of a rotational motor Object can be the current motor position
when reading, and a target position to reach when writing.
This behavior is controlled by the split slot.
Slots are automatically created when Object.setSlot is called.
- ’<<’(slot)
Bounces to >> reversing the two Slots.
- ’>>’(slot)
The >> operator connects two Slot together through a Subscription. Each time the
source Slot is updated, its new value is written to the target Slot. This function should
be used to bridge a component producing an output value to a component expecting an
input value. It returns a Subscription that can be used to configure the link, and gather
statistics.
var tick = 0|;
var tack = 0|;
var sub = &tick >> &tack;
[00000001] Slot_0x42389d88 >> Slot_0x42387c88
tack->set = function(v) { echo("tack " + v)}|;
timeout(10.5s) every|(1s) tick++,
sleep(1.5s) | sub.callCount;
[00000002] *** tack 1
[00000003] *** tack 2
[00000004] 2
sub.enabled = false| sleep(1s)| sub.enabled = true|;
sleep(2s);
[00000002] *** tack 4
[00000003] *** tack 5
sub.disconnect();
sleep(2s);
- changed
Contains an Event which is emitted each time the slot value is written to. It is used by the
system to implement the watch-expression (Listing 20.11.3).
var x = []|;
at (x->changed?) // Same thing as &x.changed
echo("x->changed");
x = [1]|;
[00092656] *** x->changed
x = [1, 2]|;
[00092756] *** x->changed
Even if the slot is assigned to the very same value, the x->changed event is emitted.
x = x|;
[00092856] *** x->changed
This is different from checking value updates. In the following example, x is not rebound to
another list, it is the content of the list that changes, therefore the changed event is not
fired:
x << 3;
[00092866] [1, 2, 3]
To monitor changes of value, use the watch-construct (Listing 20.11.3).
- constant
Defines whether a slot can be assigned a new value.
var c = 0;
[00000000] 0
c = 1;
[00000000] 1
c->constant = true;
[00000000] true
c = 2;
[00000000:error] !!! cannot modify const slot
c->constant = false;
[00000000] false
c = 3;
[00000000] 3
A new slot can be declared constant when first defined, in which case the initial value is
required.
const var two;
[00000030:error] !!! syntax error: const declaration without a value
const var two = 2;
[00000036] 2
two = 3;
[00000037:error] !!! cannot modify const slot
two->constant;
[00000038] true
- copy(targetObject, targetName)
Duplicate the Slot to the slot targetName of object targetObject.
- copyOnWrite
If set to false, disables copy on write behavior for this slot (see Section 20.4.5.3).
class a {
var x = 0;
var y = 0;
}|;
var b = a.new()|;
a.&x.copyOnWrite = false|;
b.x = 1| b.y = 1|;
assert
{
a.x == 1;
a.y == 0;
};
- get
Together with oset, this slot can be set with a function that will be called each time the slot is
accessed. If one exists, the value slot is ignored, and the value returned from get or oget is used
instead. Only one of them can be set. get is called on the slot itself with no other argument,
whereas oget is called on the object who first owned the slot, with the slot as optional
argument.
Use get when all the information needed to compute the value are in the slot itself.
var counter = 0|;
var &counter.increment = 2|;
counter->get = function()
{ value += increment | value}|;
counter;
[00000001] 2
counter;
[00000002] 4
counter->increment = 3|;
counter;
[00000003] 7
Use oget when the computation needs information in the object owning the slot. Using oget is
better than using get with a closure.
class Vector
{
var x;
var y;
function init(x_, y_)
{
x = x_;
y = y_;
};
var length;
length->oget = function() // or function(slot)
{
((x*x)+(y*y)).sqrt()
};
}|;
var v = Vector.new(2, 0)|;
v.length;
[00000001] 2
- oget
Similar to get, but with a different signature: the callback function is called on the object owning
the slot, instead of the slot itself.
The oget slot can be changed using get x syntax described in Section 7.8.
- set
Together with oset, this slot can be set with a function that will be called each time the
slot is written to. If one exists, the default behavior that simply writes the value to
value is disabled. set is called on the slot itself, with the value as its sole argument.
oset is called on the object owning the slot, with the value and optionally the slot as
arguments.
Use set when the operation performed by your function needs no information outside the
slot.
set and oset can either return the value or write it to the value slot.
var positiveInt = 0|;
positiveInt->set = function(v)
{
if (v >= 0)
v // return the value
else
{} // return void: ignore the value
}|;
positiveInt = 5 | positiveInt;
[00000001] 5
positiveInt = -1 | positiveInt;
[00000001] 5
Only the object owning the slot should use set and oset. Other objects should use changed,
watch constructs ((Listing 20.11.3)) or >>.
var integer = 0|;
integer->set = function(v) { v.round()}|;
integer = 1.6 | integer;
[00000001] 2
Use oset when you need access to the object from your function.
class Vector
{
var x;
var y;
function init(x_, y_)
{
x = x_;
y = y_;
};
var length;
length->oget = function(slot) { ((x*x)+(y*y)).sqrt() };
/// Change the vector length, keeping the same direction
length->oset = function(value) // or function(value, slotName)
{
var ratio = value / length;
x *= ratio;
y *= ratio;
{}; // return no value
};
}|;
var v = Vector.new(2, 0)|;
v.length *= 2|;
v.x;
[00000001] 4
v.y;
[00000002] 0
- oset
Similar to set but with a different signature: the function is called on the object owning the slot
instead of the slot itself. The function can take 1 or two arguments: the input value, and
optionally the name of the slot.
The oset slot can be changed using set x syntax described in Section 7.8.
- notifyAccess(onAccess)
Deprecated, use get or oget. Similar to the C++ UNotifyAccess, calls onAccess each time the
Slot is accessed (read).
var Global.counter = 0|
var Global.access = 0|
var accessHandle = Global.&access.notifyAccess(closure() {
Global.access = ++Global.counter
})|
import Global.*;
assert
{
access == 1;
access == 2;
access == 3;
};
Global.&access.removeNotifyAccess(accessHandle)|;
assert
{
access == 3;
access == 3;
};
- notifyChange(onChange)
Deprecated, use >> or changed. Similar to the C++ UNotifyChange (see Section 25.5), register
onChange and call it each time this Slot is written to. Return an identifier that can be passed to
removeNotifyChange to unregister the callback. Subscribing to the changed event has a similar
effect.
var Global.y = 0|
var handle = Global.&y.notifyChange(closure() {
echo("The value is now " + Global.y)
})|
Global.y = 12;
[00000001] *** The value is now 12
[00000002] 12
Global.&y.removeNotifyChange(handle)|;
Global.y = 13;
[00000003] 13
- notifyChangeOwned(onChangeOwned)
Deprecated, this call now just set the set slot. Similar to the C++ UNotifyChange for a split
Slot (see Section 25.5), register onChange and call it each time this UVar is written to.
Return an identifier that can be passed to removeNotifyChangeOwned to unregister the
callback.
- outputValue
The value that is returned when a read occurs, if in split mode.
- owned
True if the Slot is in split mode, that is if it contains both a sensor and a command value. This
name is for backward compatibility.
- removeNotifyAccess(id)
Disable the notification installed as id by notifyAccess.
- removeNotifyChange(id)
Disable the notification installed as id by notifyChange.
- removeNotifyChangeOwned(id)
Disable the notification installed as id by notifyChangeOwned.
- setOutputValue(val)
In split mode, update output value slot outputValue and trigger changed.
- split
Indicates that the slot has both an input value and an output value. The input value is written
to by external code, and the slot will act on it. The output value is updated by the slot itself and
made visible to external code.
This feature’s intended use is to have both a sensor value and an actuator command accessible on
the same slot.
More formally, once activated by setting the split slot to true:
Practically, code external to the slot owner continue to use the slot as usual. The object owning
the slot must:
- Directly access value, or use a setter to read the value written by external code.
- Call setOutputValue to update the value visible to external call. This call will trigger
the changed event.
class Motor
{
function init()
{
// our val is both motor command, and motor current position
var this.val = 0;
var this.running = false;
var this.runTag = Tag.new();
&val.owned = true;
// Initialize current position
&val.outputValue = 0;
// Install a setter, that will be called when ’val’ is written to.
// Use oset, so that the ’this’ passed to oset is the motor.
set val(command)
{
setCommand(command);
};
};
function setCommand(command)
{ // This function is called when someone writes ’command’ to ’val’.
var same = (command == &val.outputValue);
echo("Motor command is now " + command);
if (same && running)
{ // Target reached, stop motor control loop
runTag.stop();
running = false;
}
else if (!same && !running)
{ // Start motor control loop
detach({runMotor()});
};
// Return command so that the value gets written to the slot
command
};
function runMotor()
{ // Move current motor position toward target, one unit per second.
running = true;
runTag: while(&val.outputValue != &val.value)
{
&val.setOutputValue(
&val.outputValue + 1 * (&val.value -&val.outputValue).sign());
sleep(1s);
};
running = false;
}
}|;
var m = Motor.new();
[00000001] Motor_0x42347788
// Send a command to motor by writing to val
m.val = 5|;
[00000002] *** Motor command is now 5
sleep(1.5s);
// Read motor current position by reading val.
m.val;
[00000003] 2
// Changed triggers when current position is updated by the Motor object.
at(m.val->changed?) echo("Motor position is " + m.val);
sleep(10s);
[00000004] *** Motor position is 3
[00000005] *** Motor position is 4
[00000006] *** Motor position is 5
- type
If set, only values inheriting from type will be accepted into the slot.
var h = 0;
[00000001] 0
h->type = Float|;
h = 1;
[00000001] 1
h = "hello";
[00000002:error] !!! unexpected "hello", expected a Float
h;
[00000001] 1
- value
The underlying value.
var z = 2;
[00000001] 2
&z.value;
[00000001] 2
&z.value = 3;
[00000002] 3
z;
[00000002] 3
21.62 Socket
A Socket can manage asynchronous input/output network connections.
The following example demonstrates how both the Server and 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 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";
}
}|;
// Import lobby so that Dialog is visible from everywhere.
import this.*;
The server, an instance of Server, expects incoming connections, notified by Server.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);
echo("server: " + reply);
socket.write(reply)|
if (reply == "off")
socket.disconnect()
};
}|;
The client, an instance of 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);
echo("client: " + reply);
write(reply);
};
}|;
The server is then activated, listening to incoming connections on a port that will be chosen by the
system among 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?) | echo("done");
[00000004] *** server: Hey!
[00000005] *** client: Hey you doin’?
[00000006] *** server: Whazaaa!
[00000007] *** client: See ya.
[00000008] *** server: off
[00000008] *** done
There is a catch though: the last message was still being processed by the system, and arrived after
we waited for the client is to be disconnected:
sleep(100ms);
[00000009] *** client: off
This is because both the last message sent from the server, and the disconnection request have
arrived at the same “instant” to the client. Both are processed asynchronously, in particular the
message reception since the code used an asynchronous at (Listing 20.11.1.3). In the case of
asynchronous event handling, this is no guarantee on the order of event processing. This can be
addressed by synchronous event processing on the client side; pay extra attention to the sync qualifier
passed to at:
var syncClient =
do (Socket.new())
{
at sync (received?(var data))
{
var reply = Dialog.reply(data);
write(reply);
echo("syncClient: " + reply);
};
}|;
syncClient.connect(server.host, server.port);
echo("syncClient: " + Dialog.lines[0]);
syncClient.write(Dialog.lines[0]);
[00000003] *** syncClient: Hi!
waituntil(syncClient.disconnected?) | echo("done");
[00000004] *** server: Hey!
[00000005] *** syncClient: Hey you doin’?
[00000006] *** server: Whazaaa!
[00000007] *** syncClient: See ya.
[00000008] *** server: off
[00000008] *** syncClient: off
[00000008] *** done
This time, as one would expect at first, the *** done appears after the full dialog was
performed.
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 used by this socket. Only the default IoService is automatically
polled.
- host
The remote host of the connection.
- isConnected
Whether this is connected.
! Socket.new().isConnected;
- 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.
- 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.
- syncWrite(data)
Similar to write, but forces the operation to complete synchronously. Synchronous and
asynchronous write operations cannot be mixed.
- write(data)
Sends data trough the connection.
21.63 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 are filled with StackFrames when the is thrown.
//#push 1 "foo.u"
function inner () { throw Exception.new("test") }|;
function getStackFrame()
{
try
{
inner()
}
catch(var e)
{
e.backtrace[0]
};
}|;
//pop
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 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
- asString
Clean display of the call location.
getStackFrame();
[00000004] foo.u:7.5-11: inner
- location
Location of the function call.
getStackFrame().location;
[00000003] foo.u:7.5-11
- name
String, representing the name of the called function.
getStackFrame().name;
[00000002] "inner"
21.64 Stream
This is used to factor code between InputStream and OutputStream.
Streams are not meant to be built, rather, use InputStream or OutputStream.
When a stream (OutputStream or InputStream) is opened on a File, that File cannot be removed.
On Unix systems, this is handled gracefully (the references to the file are removed, but the content is
still there for the streams that were already bound to this file); so in practice, the File appears to be
removable. On Windows, the File cannot be removed at all. Therefore, do not forget to close the
streams you opened.
21.65 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 20.1.6.6 for details and examples.
A null String can also be obtained with String.new.
String.new() == "";
String == "";
"123".new() == "123";
- ’%’(args)
It is an equivalent of Formatter.new(this) % args. See Formatter and FormatInfo, which
provide more examples.
"%s + %s = %s" % [1, 2, 3] == "1 + 2 = 3";
var f = "%10s level: %-4.1f%%";
f % ["Battery", 83.3] == " Battery level: 83.3%";
f % [ "Sound", 60 ] == " Sound level: 60 %";
- ’*’(n)
Concatenate thisn 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");
- ’==’(that)
Whether this and that are the same string.
"" == ""; !("" != "");
!("" == "\0"); "" != "\0";
"0" == "0"; !("0" != "0");
!("0" == "1"); "0" != "1";
!("1" == "0"); "1" != "0";
- ’[]’(from, to = from + 1)
The sub-string starting at from, up to and not including to.
"foobar"[0, 3] == "foo";
"foobar"[0] == "f";
The indexes must be integers from -size up to size + 1 (inclusive). Independently of their order
as integers, from must be before or equal to to.
"foobar"[1.1];
[00051825:error] !!! []: invalid index: 1.1
"foobar"[-7];
[00051841:error] !!! []: invalid index: -7
"foobar"[6];
[00051853:error] !!! []: invalid index: 6
"foobar"[3, 1];
[00051953:error] !!! []: range starting after its end: 3, 1
Negative indexes means “from the end”.
"foobar"[-1] == "r";
"foobar"[-6] == "f";
"foobar"[-1, 0] == "r";
"foobar"[-6, -3] == "foo";
// Valid since position 2 is before position -1.
"foobar"[2, -1] == "oba"
- ’[]=’(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. The condition on from and to are the same of for ’[]’. 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";
s1 == "quuxbar";
s2 == "quuxbar";
(s1[4, 7] = "") == "";
s2 == "quux";
(s1[-3, -1] = "UU") == "UU";
s1 == "qUUx";
(s1[-1] = "X") == "X";
s1 == "qUUX";
};
- asFloat
The value of this as a Float. See Section 20.1.6.4 for their syntax.
"23".asFloat() == 23;
"23.03".asFloat() == 23.03;
"123_456_789".asFloat() == 123_456_789;
"12_34_56_78_90".asFloat() == 12_34_56_78_90;
"1_2__3___45".asFloat() == 1_2__3___45;
"1_2.3__4".asFloat() == 1_2.3__4;
"0xFFFF_FFFF".asFloat() == 0xFFFF_FFFF;
"1e1_0".asFloat() == 1e1_0;
Raise an error on invalid numbers.
"123abc".asFloat();
[00000001:error] !!! asFloat: invalid number: "123abc"
"0xabcdefg".asFloat();
[00061848:error] !!! asFloat: invalid number: "0xabcdefg"
"1.2_".asFloat();
[00048342:error] !!! asFloat: invalid number: "1.2_"
- asList
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
this as a literal (escaped) string.
"foo".asPrintable() == "\"foo\"";
"foo".asPrintable().asPrintable() == "\"\\\"foo\\\"\"";
- asString
this.
var s = "\"foo\"";
s.asString() === s;
- closest(set)
The closest (in the sense of distance) string in set 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)
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;
"foo".distance("fozo") == 1; // Deletion.
"fozo".distance("foo") == 1; // Insertion.
"ofzo".distance("fozo") == 1; // Transposition.
"fpzo".distance("fozo") == 1; // Substitution.
"fpzzo".distance("fozo") == 2; // Substitution and insertion.
- empty
Whether this is the empty string.
- find(pattern, position = 0)
Search for the pattern string in this starting from the position. When the pattern is not
found -1 is returned.
"Hello, World!".find("o") == 4;
"Hello, World!".find("o", 4) == 4;
"Hello, World!".find("o", 5) == 8;
"Hello, World!".find("o", 9) == -1;
- fresh
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";
- fromAscii(v)
The character corresponding to the integer v according to the ASCII coding. See also toAscii.
String.fromAscii( 97) == "a";
String.fromAscii( 98) == "b";
String.fromAscii(0xFF) == "\xff";
[0, 1, 2, 254, 255]
.map(function (var v) { String.fromAscii(v) })
.map(function (var v) { v.toAscii() })
== [0, 1, 2, 254, 255];
- hash
A Hash object corresponding to this string value. Equal strings (in the sense of ’==’) have equal
hashes. See Object.hash.
"".hash().isA(Hash);
"foo".hash() == "foo".hash();
"foo".hash() != "bar".hash();
- isAlnum
Whether all the characters of this are digits or/and characters (see Listing 21.1).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ASCII values | Characters | isCntrl | isSpace | isBlank | isUpper | isLower | isAlpha | isDigit | isXdigit | isAlnum | isPunct | isGraph | isPrint |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0x00 .. 0x08 | | ∙ | | | | | | | | | | | |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0x09 | ∖t | ∙ | ∙ | ∙ | | | | | | | | | |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0x0A .. 0x0D | ∖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) | ∙ | | | | | | | | | | | |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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).
Note the following equivalences:
isAlnum ≡ | isAlpha || isDigit |
isAlpha ≡ | isLower || isUpper |
isGraph ≡ | isAlpha || isDigit || isPunct |
≡ |
Table 21.1: Character handling functions
"".isAlnum();
"123".isAlnum();
"abc".isAlnum(); "1b".isAlnum();
- isAlpha
Whether all the characters of this are letters, upper or lower case (see Listing 21.1).
"".isAlpha();
"abcABC".isAlpha();
!"123".isAlpha(); "b".isAlpha();
- isAscii
Whether all the characters of this are ascii characters (see Listing 21.1).
"".isAscii();
"abc123".isAscii();
!"
é
".isAscii(); !"
è
".isAscii();
- isBlank
Whether all the characters of this are spaces or tabulations (see Listing 21.1).
"".isBlank();
"\t \t ".isBlank();
!"123".isBlank(); !"\v".isBlank();
- isCntrl
Whether all the characters of this are control characters (non-printable) (see Listing 21.1).
"".isCntrl();
"\t\n\f".isCntrl();
"\10".isCntrl(); !"abc".isCntrl();
- isDigit
Whether all the characters of this are decimal digits (see Listing 21.1).
"".isDigit();
"0123456789".isDigit();
!"a".isDigit(); !"0x10".isDigit();
- isGraph
Whether all the characters of this are printable characters (not a space) (see Listing 21.1).
"".isGraph();
"abc123".isGraph();
"{|}".isGraph();
!"\t\n\r".isGraph(); !" ".isGraph();
- isLower
Whether all the characters of this are lower characters (see Listing 21.1).
"".isLower();
"abc".isLower();
!"123".isLower(); !"A".isLower();
- isPrint
Whether all the characters of this are printable characters (see Listing 21.1).
"".isPrint();
"abcd1234".isPrint();
"{|}".isPrint(); !"\r".isPrint();
- isPunct
Whether all the characters of this are punctuation marks (see Listing 21.1).
"".isPunct();
"!_[]".isPunct();
!"abc".isPunct(); !"a".isPunct();
- isSpace
Whether all the characters of this are spaces (see Listing 21.1).
"".isSpace();
" ".isSpace();
!" a ".isSpace();
- isUpper
Whether all the characters of this are upper characters (see Listing 21.1).
"".isUpper();
"ABC".isUpper();
!"123".isUpper(); !"a".isUpper();
- isXdigit
Whether all the characters of this are hexadecimal digits (see Listing 21.1).
"".isXdigit();
"abcdef".isXdigit();
"0123456789".isXdigit();
!"g".isXdigit(); "123abc".isXdigit();
- 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]";
- length
The number of characters in the string. Currently, this is a synonym of size.
"foo".length == 3;
"".length == 0;
- 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 !";
- rfind(pattern, position = -1)
Search backward for the pattern string in this starting from the position. To denote the end
of the string, use -1 as position. When the pattern is not found -1 is returned.
"Hello, World!".rfind("o") == 8;
"Hello, World!".rfind("o", 8) == 8;
"Hello, World!".rfind("o", 7) == 4;
"Hello, World!".rfind("o", 3) == -1;
"Hello, World!".rfind("o", -1) == 8;
- size
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"];
- toAscii
Convert the first character of this to its integer value in the ASCII coding. See also fromAscii.
"a".toAscii() == 97;
"b".toAscii() == 98;
"\xff".toAscii() == 0xff;
"Hello, World!\n"
.asList()
.map(function (var v) { v.toAscii() })
.map(function (var v) { String.fromAscii(v) })
.join()
== "Hello, World!\n";
- toLower
A String which is this with upper case letters converted to lower case. See also toLower.
var hello = "Hello, World!";
hello.toLower() == "hello, world!";
hello == "Hello, World!";
- toUpper
A String which is this with lower case letters converted to upper case. See also toLower.
var hello = "Hello, World!";
hello.toUpper() == "HELLO, WORLD!";
hello == "Hello, World!";
21.66 Subscription
Connection between InputPorts, see Section 25.7.
- asynchronous
If true, notifies on the target InputPort will trigger asynchronously. The system will also
prevent two instances from running in parallel by dropping updates until the callback
functions terminate.
- callCount
Number of times the link was reset.
- disconnect
Disconnect the link.
- enabled
Set to false to disable the link.
- fireRate
Rate in Hertz at which the link triggers.
- getAll
The list of all the connections in the system.
- maxCallTime
Maximum call time.
- meanCallTime
Average time taken by the callback function on the target InputPort.
- minCallTime
Minimum call time.
- minInterval
Minimal interval in seconds at which the link can activate. Changes of the source at a
higher rate will be ignored.
- reconnect(src)
Reconnect the link by changing the source to src.
- resetStats
Reset all statistics.
21.67 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.
- arguments
The list of the command line arguments passed to the user script. This is especially useful in
scripts.
$ cat >echo <<EOF
#! /usr/bin/env urbi
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 20.9.
’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 20.9.
- currentRunner
An obsolete alias for Job.current.
- 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);
};
- env
A Dictionary containing the current environment of Urbi. See also env.init.
(env["MyVar"] = 12) == "12";
env["MyVar"] == "12";
// A child process that uses the environment variable.
System.system("exit $MyVar") >> 8 ==
{if (Platform.isWindows) 0 else 12};
(env["MyVar"] = 23) == "23";
System.system("exit $MyVar") >> 8 ==
{if (Platform.isWindows) 0 else 23};
// Defining to empty is defining, unless you are on Windows.
(env["MyVar"] ="") == "";
env["MyVar"].isNil == Platform.isWindows;
env["UndefinedEnvironmentVariable"].isNil;
!env["PATH"].isNil;
(env["MyVar"] = 12) == "12";
!env["MyVar"].isNil;
env.erase("MyVar") == "12";
env["MyVar"].isNil;
- env.init
Refresh the Urbi environment by fetching all the environment variables. Beware that
env is not updated when calling getenv, setenv or unsetenv from the C library.
Initialize it first and then manipulate your environment as a simple Dictionary.
env.init() == env;
!env["USER"].isNil;
- eval(source, target = this)
Evaluate the urbiscriptsource, and return its result. See also loadFile. 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) or target if specified. In
particular, to create local variables, create scopes.
// Create a slot in the current object.
eval("var a = 23;") == 23;
this.a == 23;
eval("var a = 3", Global) == 3;
Global.a == 3;
Exceptions are thrown on error (including syntax errors).
// Scanner errors.
eval("#");
[00000004:error] !!! 1.1: syntax error: invalid character: ‘#’
[00000005:error] !!! called from: eval
// Syntax errors.
eval("3; 1 * * 2");
[00000002:error] !!! 1.8: syntax error: unexpected *
[00000003:error] !!! called from: eval
// Exceptions.
eval("1/0");
[00008316:error] !!! 1.1-3: /: division by 0
[00008316:error] !!! called from: eval
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";
}
};
Warnings are reported.
eval("new Object");
[00001388:warning] !!! 1.1-10: ‘new Obj(x)’ is deprecated, use ‘Obj.new(x)’
[00001388:warning] !!! called from: eval
[00001388] Object_0x1001b2320
Nested calls to eval behave as expected. The locations in the inner calls refer to the position
inside the evaluated string.
eval("/");
[00001028:error] !!! 1.1: syntax error: unexpected /
[00001028:error] !!! called from: eval
eval("eval(\"/\")");
[00001032:error] !!! 1.1: syntax error: unexpected /
[00001032:error] !!! called from: 1.1-9: eval
[00001032:error] !!! called from: eval
eval("eval(\"eval(\\\"/\\\")\")");
[00001035:error] !!! 1.1: syntax error: unexpected /
[00001035:error] !!! called from: 1.1-9: eval
[00001035:error] !!! called from: 1.1-19: eval
[00001035:error] !!! called from: eval
- getenv(name)
Deprecated function use env[name] instead. The value of the environment variable name as a
String if set, nil otherwise. See also env, setenv and unsetenv.
getenv("UndefinedEnvironmentVariable").isNil;
[01234567:warning] !!! ‘System.getenv(that)’ is deprecated, use ‘System.env[that]’
!getenv("PATH").isNil;
[01234567:warning] !!! ‘System.getenv(that)’ is deprecated, use ‘System.env[that]’
- getLocale(category)
A String denoting the locale set for category, or raise an error. See setLocale for more details.
getLocale("LC_IMAGINARY");
[00006328:error] !!! getLocale: invalid category: LC_IMAGINARY
- load(file, target = this)
Look for file in the Urbi path (Section 19.1), and load it in the context of target. See also
loadFile. Throw a Exception.FileNotFound error if the file cannot be found. Return the last
value of the file.
// Create the file ‘‘123.u’’ that contains exactly ‘‘var t = 123;’’.
File.save("123.u", "var this.t = 123;");
assert
{
load("123.u") == 123;
this.t == 123;
load("123.u", Global) == 123;
Global.t == 123;
};
- loadFile(file, target = this)
Load the urbiscript file file in the context of target. Behaves like eval applied to the content
of file. Throw a Exception.FileNotFound error if the file cannot be found. Return the last
value of the file.
// Create the file ‘‘123.u’’ that contains exactly ‘‘var y = 123;’’.
File.save("123.u", "var y = 123;");
assert
{
loadFile("123.u") == 123;
this.y == 123;
loadFile("123.u", Global) == 123;
Global.y == 123;
};
- loadLibrary(library)
Load the library library, to be found in UObject.searchPath. The library may be a String
or a Path. The C++ symbols are made available to the other C++ components. See also
loadModule.
- loadModule(module)
Load the UObjectmodule. 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 19.1). If the file is found announce on Channel that
file is about to be loaded, and load it.
// Create the file ‘‘123.u’’ that contains exactly ‘‘123;’’.
File.save("123.u", "123;");
assert
{
maybeLoad("123.u") == 123;
maybeLoad("u.123").isVoid;
};
- ndebug
If true, do not evaluate the assertions. See Section 20.9.
function one() { echo("called!"); 1 }|;
assert(!System.ndebug);
assert(one);
[00000617] *** called!
// Beware of copy-on-write.
System.ndebug = true|;
assert(one);
System.ndebug = false|;
assert(one);
[00000622] *** called!
- PackageInfo
See System.PackageInfo.
- period
The period of the Urbi kernel. Influences the trajectories (TrajectoryGenerator), and the
UObject monitoring. Defaults to 20ms.
- Platform
See System.Platform.
- profile(function)
Compute some measures during the execution of function and return the results
as a Profile object. A Profile details information about time, function calls and
scheduling.
- programName
The path under which the Urbi process was called. This is typically ‘.../urbi’ (Section 19.3) or
‘.../urbi-launch’ (Section 19.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;
// Not an error
var Global.x = 1;
echo(Global.x);
};
[00000002] *** 1
// redefinitionMode applies only to the scope.
var Global.x = 0;
[00000003:error] !!! slot redefinition: x
- requireFile(file, target)
Load file in the context of target if it was not loaded before (with load or requireFile).
Unlike load, requireFile always returns void. If file is being loaded concurrently
requireFile waits until the loading is finished.
// Create the file ‘‘test.u’’ that echoes a string.
File.save("test1.u", "echo(\"test 1\"); 1;");
requireFile("test1.u");
[00000001] *** test 1
requireFile("test1.u");
// File is not re-loaded
File.save("test2.u", "echo(\"test 2\"); 2;");
load("test2.u");
[00000004] *** test 2
[00000004] 2
requireFile("test2.u");
load("test2.u");
[00000006] *** test 2
[00000006] 2
The target is not taken into account to check whether the file has already been loaded: if you
require twice the same file with two different targets, it will be loaded only for the
first.
requireFile("test2.u", Global);
- resetStats
Reinitialize the stats computation.
0 < System.stats()["cycles"];
System.resetStats().isVoid;
1 == System.stats()["cycles"];
- scopeTag
Bounce to Tag.scope.
- searchFile(file)
Look for file in the searchPath and return its Path. Throw a Exception.FileNotFound error
if the file cannot be found.
// Create the file ‘‘123.u’’ that contains exactly ‘‘123;’’.
File.save("123.u", "123;");
assert
{
searchFile("123.u") == Path.cwd / Path.new("123.u");
};
- searchPath
The Urbi path (i.e., the directories where the urbiscript files are looked for, see Section 19.1) as a
List of Paths.
System.searchPath.isA(List);
System.searchPath[0].isA(Path);
- setenv(name, value)
Deprecated, use env[name] = value instead. Set the environment variable name to
value.asString, and return this value. See also env, getenv and unsetenv.
Windows Issues
Under Windows, setting to an empty value is equivalent to making undefined.
setenv("MyVar", 12) == "12";
[00000001:warning] !!! ‘System.setenv(var, value)’ is deprecated, \
use ‘System.env[var] = value’
env["MyVar"] == "12";
- setLocale(category, locale = "")
Change the system’s locale for the category to locale and return void. If locale is empty, then
use the locale specified by the user’s environment (e.g., the environment variables). The
category can be:
-
LC_ALL
-
Overrides all the following categories.
-
LC_COLLATE
-
Controls how string sorting is performed.
-
LC_CTYPE
-
Change what characters are considered as letters and so on.
-
LC_MESSAGES
-
The catalog of translated messages. This category is not supported by Microsoft
Windows.
-
LC_MONETARY
-
How to format monetary values.
-
LC_NUMERIC
-
Set a locale for formatting numbers.
-
LC_TIME
-
Set a locale for formatting dates and times.
With urbi is run, it does not change its locales: it defaults to the “good old C mode”, which
corresponds to the ‘C’ (or ‘POSIX’) locale. See also getLocale.
// Initially they are all set to "C".
getLocale("LC_ALL") == "C";
getLocale("LC_CTYPE") == "C";
getLocale("LC_NUMERIC") == "C";
// Windows does not understand the "fr_FR" locale, it supports "French"
// which actually denotes "French_France.1252".
var fr_FR =
{ if (System.Platform.isWindows) "French_France.1252" else "fr_FR.utf8" };
// Changing one via the environment does not affect the others.
(env["LC_CTYPE"] = fr_FR) == fr_FR;
getLocale("LC_CTYPE") == "C";
setLocale("LC_CTYPE").isVoid;
getLocale("LC_CTYPE") == fr_FR;
getLocale("LC_NUMERIC") == "C";
// Changing one via setLocale does not change the others either.
setLocale("LC_CTYPE", "C").isVoid;
getLocale("LC_CTYPE") == "C";
getLocale("LC_NUMERIC") == "C";
// The environment variable LC_ALL overrides all the others.
env["LC_ALL"] = fr_FR;
setLocale("LC_ALL").isVoid;
getLocale("LC_ALL") == fr_FR;
getLocale("LC_CTYPE") == fr_FR;
getLocale("LC_NUMERIC") == fr_FR;
// Explicit changes of LC_ALL overrides all the others.
setLocale("LC_ALL", "C").isVoid;
getLocale("LC_ALL") == "C";
getLocale("LC_CTYPE") == "C";
getLocale("LC_NUMERIC") == "C";
On invalid requests, raise an error.
setLocale("LC_IMAGINARY");
[00006328:error] !!! setLocale: invalid category: LC_IMAGINARY
env["LC_ALL"] = "elfic"|;
setLocale("LC_ALL");
[00024950:error] !!! setLocale: cannot set locale LC_ALL to elfic
setLocale("LC_ALL", "klingon");
[00074958:error] !!! setLocale: cannot set locale LC_ALL to klingon
- shiftedTime
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(exit_status = 0)
Have the Urbi server shut down, with exit status exit_status. All the connections are closed,
the resources are released. Architecture dependent.
- sleep(duration = inf)
Suspend the execution for duration seconds. No CPU cycle is wasted during this wait. If no
duration is given the execution is suspended indefinitely.
(time - {sleep(1s); time}).round() == -1;
- spawn(function, clear)
Deprecated internal function. Bounces to function.spawn(clear), see Code.spawn.
System.spawn(closure () { echo(1) }, true).isA(Job);
[00016657:warning] !!! ‘System.spawn’ is deprecated, use ‘Code.spawn’
[00016659] *** 1
- stats
A Dictionary containing information about the execution cycles of Urbi. This is an internal
feature made for developers, it might be changed without notice. See also resetStats. These
statistics make no sense in ‘--fast’ mode (Section 19.3.1).
var stats = System.stats();
stats.isA(Dictionary);
stats.keys.sort() == ["cycles",
"cyclesMin", "cyclesMean", "cyclesMax",
"cyclesVariance", "cyclesStdDev"].sort();
// Number of cycles.
0 < stats["cycles"];
// Cycles duration.
0 <= stats["cyclesMin"] <= stats["cyclesMean"] <= stats["cyclesMax"];
stats["cyclesVariance"].isA(Float);
stats["cyclesStdDev"].isA(Float);
- 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
The number of seconds elapsed since the Urbi server was launched. See also Date. In presence of
a frozen 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();
};
- timeReference
The “origin of time” of this run of Urbi, as a Date. It is a constant during the run.
Basically, System.time is about Date.now - System.timeReference. See also time and
Date.now.
var t1 = System.timeReference|;
sleep(1s);
var t2 = System.timeReference|;
assert
{
t1 == t2;
t1.isA(Date);
(Date.now - (System.timeReference + System.time)) < 0.5s;
};
- unsetenv(name)
Deprecated use env.erase (name) instead. Undefine the environment variable name, return its
previous value. See also env, getenv and setenv.
(env["MyVar"] = 12) == "12";
!env["MyVar"].isNil;
unsetenv("MyVar") == "12";
[01234567:warning] !!! ‘System.unsetenv(var)’ is deprecated, use ‘System.env.erase(var)’
env["MyVar"].isNil;
- version
The version of Urbi SDK. A string composed of two or more numbers separated by periods:
‘"3.0.0"’.
System.version in Regexp.new("\\d+(\\.\\d+)+");
21.68 System.PackageInfo
Information about Urbi SDK and its components.
- bugReport
The address where to send bug reports.
System.PackageInfo.components["Urbi"].bugReport
== "kernel-bugs@lists.gostai.com";
- components
A Dictionary of the components loaded in the package. The component "Urbi SDK" duplicates
the component "Urbi" for backward compatibility: prefer the latter.
System.PackageInfo.components.keys
== ["Libport", "Urbi", "Urbi SDK"];
System.PackageInfo.components["Urbi SDK"]
== System.PackageInfo.components["Urbi"];
- copyrightHolder
Who owns the copyright of the package.
System.PackageInfo.components["Urbi"].copyrightHolder
== "Gostai S.A.S.";
- copyrightYears
The years that the copyright covers.
System.PackageInfo.components["Urbi"].copyrightYears
== "2004-2012";
- date
A string corresponding to the source date (day and time).
System.PackageInfo.components["Urbi"].date.isA(String);
- day
The day part of the date string.
System.PackageInfo.components["Urbi"].day.isA(String);
- description
The complete description string, with slashes.
System.PackageInfo.components["Urbi"].description.isA(String);
- id
The identification string.
System.PackageInfo.components["Urbi"].id.isA(String);
- major
The major version component (see version), for instance 2.
System.PackageInfo.components["Urbi"].major.isA(Float);
- minor
The minor version component (see version), for instance 7.
System.PackageInfo.components["Urbi"].minor.isA(Float);
- name
The name of the package as a String.
System.PackageInfo.components["Urbi"].name
== "Urbi";
- patch
The number of changes since the version.
System.PackageInfo.components["Urbi"].patch.isA(Float);
- revision
The revision string.
System.PackageInfo.components["Urbi"].revision.isA(String);
- string
Name and version concatenated, for instance "Urbi 3.0.0".
System.PackageInfo.components["Urbi"].string.isA(String);
- subMinor
The sub-minor version component (see version), for instance 4 for Urbi SDK 2.7.4.
System.PackageInfo.components["Urbi"].subMinor.isA(Float);
- tarballVersion
The complete version string, with dashes.
System.PackageInfo.components["Urbi"].tarballVersion.isA(String);
- tarname
Name of the tarball.
System.PackageInfo.components["Urbi"].tarname
== "urbi-sdk";
- version
The version string, such as "2.7" or "2.7.4".
do (System.PackageInfo.components["Urbi"])
{
assert
{
version ==
{
if (subMinor)
"%s.%s.%s" % [major, minor, subMinor]
else
"%s.%s" % [major, minor]
};
};
}|;
- versionRev
Version and revision together.
do (System.PackageInfo.components["Urbi"])
{
assert
{
versionRev
== "version %s patch %s revision %s"
% [version, patch, revision];
};
}|;
- versionValue
The version as an integer, for instance 2007004 for 2.7.4.
do (System.PackageInfo.components["Urbi"])
{
assert
{
versionValue == (major * 1e6 + minor * 1e3 + subMinor);
};
}|;
21.69 System.Platform
A description of the platform (the computer) the server is running on.
- host
The type of system Urbi SDK runs on.
- hostAlias
The name of the system Urbi SDK runs on as the person who compiled it decided to name it.
Typically empty, it is fragile to depend on it.
System.Platform.hostAlias.isA(String);
- hostName
The name of the machine, as given by the uname -n command. Unix only.
if (!System.Platform.isWindows)
assert(System.Platform.hostName == System.system("uname -n"));
- hostOs
The OS type of system Urbi SDK runs on. For instance darwin9.8.0 or linux-gnu or
mingw32.
- isWindows
Whether running under Windows.
System.Platform.isWindows in [true, false];
- kind
Either "POSIX" or "WIN32".
System.Platform.kind in ["POSIX", "WIN32"];
21.70 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 10.3.
21.70.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();
// Nothing runs.
sleep(2.2s);
t: every(1s) echo("bar"),
sleep(2.2s);
[00000158] *** bar
[00001159] *** bar
[00002159] *** bar
t.stop();
Tag.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");
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"),
// Neither new nor old code runs.
ping.unblock();
assert(!ping.blocked);
sleep(2.1s);
// But we can use the tag again.
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.
{
var t = Tag.new();
var res = [];
for (3)
detach(res << {t: sleep()});
t.block("foo");
res;
}
==
["foo", "foo", "foo"];
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);
// The tag is frozen, nothing is run.
// Unfreeze the tag: suspended code is resumed.
// Note the difference between "time" and "shiftedTime".
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 stopped when the execution reaches the end of
the current scope. This is handy to implement cleanups, however 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 tagged
statements.
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 provides 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()
{
turnGasOn();
// Cooking code ...
turnGasOff();
}|
enterTheKitchen();
cook();
leaveTheKitchen();
};
This cook function 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()
{
turnGasOn();
if (mealReady)
{
echo("The meal is already there, nothing to do!");
// Oops ...
return;
};
for (var i in recipe)
if (i in kitchen)
putIngredient(i)
else
// Oops ...
throw Exception("missing ingredient: %s" % i);
// ...
turnGasOff();
}|
};
Here, if the meal was already prepared, or if an ingredient is missing, we will leave the cook
function without executing the turnGasOff statement, through the return statement or the exception.
One correct way to ensure gas is necessarily turned off is:
{
function cook()
{
var withGas = Tag.new("withGas");
at (withGas.enter?)
turnGasOn();
// Even if exceptions are thrown or return is called,
// the gas will be turned off.
at (withGas.leave?)
turnGasOff();
withGas: {
// Cooking code...
}
}|
};
If you need your enter/leave functions to be called synchronously and very efficiently, you can as an
alternative to enter/leave define the ılocal slots onEnter and onLeave.
{
function cook()
{
var withGas = Tag.new("withGas");
var withGas.onEnter = turnGasOn; // copy the function
var withGas.onLeave = turnGasOff;
withGas : {
// Cooking code...
}|
}|
};
The onEnter and onLeave must be local slots, inheritence will not work for them.
Alternatively, the try/finally construct provides an elegant means to achieve the same result
(Section 20.8.4).
{
function cook()
{
try
{
turnGasOn();
// Cooking code...
}
finally
{
// Even if exceptions are thrown or return is called,
// the gas will be turned off.
turnGasOff();
}
}|
};
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 21.70.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
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() |
// Stopping parent also stops children.
{
parent: {sleep(100ms); echo("parent")},
child: {sleep(100ms); echo("child")},
parent.stop();
sleep(200ms);
echo("end");
};
[00000001] *** end
// Stopping child has no effect on parent.
{
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; // Tagged by a
a.b: bar; // Tagged by a and b
a.b.c: baz; // Tagged by a, b and c
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.
// Anonymous tag.
var t1 = Tag.new();
[00000001] Tag<tag_77>
// Named tag.
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 21.70.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 21.70.1.2.
- blocked
Whether code tagged by this is blocked. See Section 21.70.1.2.
- end
A sub-tag that prints out ”tag_name: end” each time flow control leaves the tagged code.
See Section 21.70.1.6.
- enter
An event triggered each time the flow control enters the tagged code. See Section 21.70.1.5.
- freeze
Suspend code tagged by this, already running or forthcoming. Frozen code can be later
unfrozen using unfreeze. See Section 21.70.1.3.
- frozen
Whether the tag is frozen. See Section 21.70.1.3.
- leave
An event triggered each time flow control leaves the tagged code. See Section 21.70.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 21.70.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 21.70.1.1.
- unblock
Unblock this. See Section 21.70.1.2.
- unfreeze
Unfreeze code tagged by this. See Section 21.70.1.3.
21.71 Timeout
Timeout objects can be used as Tags to execute some code in limited time. See also the timeout
construct (Listing 20.10.7).
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] !!! new: Timeout_0x133ec0 has timed out.
[00000007:error] !!! called from: ---- event handler backtrace:
[00000007:error] !!! called from: new
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] !!! new: Timeout_0x133ec0 has timed out.
[00000007:error] !!! called from: ---- event handler backtrace:
[00000007:error] !!! called from: new
t: { echo("Open"); sleep(1s); echo("Close"); };
[00000007] *** Open
[00000007:error] !!! new: Timeout_0x133ec0 has timed out.
[00000007:error] !!! called from: ---- event handler backtrace:
[00000007:error] !!! called from: new
Even if exceptions have been disabled, you can check whether the count-down expired with
timedOut.
t:sleep(500ms);
[00000007:error] !!! new: Timeout_0x133ec0 has timed out.
[00000007:error] !!! called from: ---- event handler backtrace:
[00000007:error] !!! called from: new
if (t.timedOut)
echo("The Timeout expired.");
[00000000] *** The Timeout expired.
At construction, a Timeout takes a duration, and a 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_0x953c1e8
- asTimeout
Return this.
var t = Timeout.new(10);
Timeout.asTimeout() === Timeout;
t.asTimeout() === t;
- end
Stop this, return void. Can be called several times. See launch.
var t = Timeout.new(10);
!t.running;
t.end().isVoid;
!t.running;
t.launch().isVoid;
t.running;
t.end().isVoid;
t.end().isVoid;
- launch
Fire this, return void. See end.
var t = Timeout.new(10);
t.launch().isVoid;
- running
Whether is currently running.
var t = Timeout.new(10);
!t.running;
t.launch().isVoid;
t.running;
t.end().isVoid;
!t.running;
- timedOut
Whether this has timed out.
var t = Timeout.new(100ms);
t.launch().isVoid;
!t.timedOut;
sleep(200ms).isVoid;
t.timedOut;
21.72 Traceable
Objects that have a concept of backtrace.
This object, made to serve as prototype, provides a definition of backtrace which can be filtered
based on the desired level of verbosity.
This prototype is not made to be constructed.
- backtrace
A call stack as a List of StackFrames. Used by Exception.backtrace and Job.backtrace.
try
{
[1].map(closure (v) { throw Exception.new("Ouch") })
}
catch (var e)
{
for| (var sf: e.backtrace)
echo(sf.name)
};
[00000001] *** map
- hideSystemFiles
Remove system files from the backtrace if this value equals true. Defaults to true.
Traceable.hideSystemFiles = false |
try
{
[1].map(closure (v) { throw Exception.new("Ouch") })
}
catch (var e)
{
for| (var sf: e.backtrace)
echo(sf.name)
};
[00000002] *** f
[00000003] *** each|
[00000003] *** []
[00000004] *** map
21.73 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 20.12).
The Accel trajectory reaches a target value at a fixed acceleration (accel attribute).
21.73.1.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.
21.73.1.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.
21.73.1.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. 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 20.12 for details about trajectories, and see Section 21.73.1
for an extensive set of examples.
- Accel
This class implements the Accel trajectory (Section 21.73.1.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 21.73.1.2, Section 21.73.1.3).
It derives from OpenLoop.
- Smooth
This class implements the Smooth trajectory (Section 21.73.1.4). It derives from OpenLoop.
- SpeedAdaptive
This class implements the Speed trajectory when the adaptive attribute is given
(Section 21.73.1.5). It derives from ClosedLoop.
- Time
This class implements the non-adaptive Speed and Time trajectories (Section 21.73.1.5,
Section 21.73.1.6). It derives from OpenLoop.
- TimeAdaptive
This class implements the Time trajectory when the adaptive attribute is given
(Section 21.73.1.6). It derives from ClosedLoop.
21.74 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] !!! new: expected 3 arguments, given 2
Triplet.new(1, 2, 3, 4);
[00000003:error] !!! new: 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;
21.75 Tuple
A tuple is a container storing a fixed number of objects. Examples include Pair and
Triplet.
The Tuple object is not meant to be instantiated, its main purpose is to share code for its descendants,
such as 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.
// not a Tuple
(1);
[00000000] 1
// Tuples
();
[00000000] ()
(1,);
[00000000] (1,)
(1, 2);
[00000000] (1, 2)
(1, 2, 3, 4,);
[00000000] (1, 2, 3, 4)
- ’*’(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");
- ’<’(other)
Lexicographic comparison between two tuples.
(0, 0) < (0, 1);
(0, 0) < (1, 0);
(0, 1) < (1, 0);
(1, 2, 3) < (2,);
- ’==’(other)
Whether this and other have the same contents (equality-wise).
(1, 2) == (1, 2);
!((1, 1) == (2, 2));
- ’[]’(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);
};
};
- 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)";
- hash
A Hash object corresponding to this tuple value. Equal tuples (in the sense of ’==’) have equal
hashes, see Object.hash.
().hash().isA(Hash);
().hash() == ().hash();
(1, "foo").hash() == (1, "foo").hash();
// Tuple and List are hashed differently.
(0, 1).hash != [0, 1].hash();
// Tuple uses a List to store elements.
(0, 1).members.hash() == [0, 1].hash();
(0, 1).hash() != (1, 0).hash();
- matchAgainst(handler, pattern)
Pattern matching on members. See Pattern.
{
// Match a tuple.
(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;
21.76 UObject
UObject is used by the UObject API (see Listing IV) to represent a bound C++ instance.
All the UObjects are copied under a unique name as slots of Global.uobjects.
21.77 uobjects
This object serves only to store the UObjects that are bound into the system (plug or
remote).
- asuobjects
Return this.
- clearStats
Reset counters.
- connectionStats
- enableStats(bool)
Depending on bool, enable or disable the statistics gathering.
- getStats
Return a dictionary of all bound C++ functions called, including timer callbacks, along
with the average, min, max call durations, and the number of calls.
- resetConnectionStats
- searchPath
- setTrace(bool)
21.78 UValue
The UValue object is used internally by the UObject API and is mostly hidden from the user. Do
not depend on it.
- asPrintable
- asString
- asTopLevelPrintable
- asUValue
- extract
- extractAsToplevelPrintable
- invalidate
- put
- transparent
21.79 UValueSerializable
This pseudo-class is made to be derived from. It provides support to exchange data between C++
and urbiscript via uvalueSerialize. See Section 25.18.2.
For the conversion to/from C++ to work properly, your class must be globally accessible and inherit
from UValueSerializable.
class Point: UValueSerializable
{
var x = 0;
var y = 0;
function init(var xx = 0, var yy = 0)
{
x = xx|
y = yy
};
function asString()
{
"<%s, %s>" % [x, y]
};
}|;
- uvalueSerialize
Return a String that corresponds to a serialization of this.
123.uvalueSerialize() == 123;
"ab".uvalueSerialize() == "ab";
[1, 2].uvalueSerialize() == [1, 2];
["b" => 2, "a" => 1].uvalueSerialize() == ["a" => 1, "b" => 2];
Point.uvalueSerialize()
== ["$sn" => "Point", "type" => "Point", "x" => 0, "y" => 0];
Point.new(12, 34).uvalueSerialize()
== ["$sn" => "Point", "x" => 12, "y" => 34];
21.80 Vector
The Vector constructor can be either be given a single List (of Floats), or any number of
Floats.
Vector can be constructed by using either Vector.new, or the literal syntax < >. However only a
subset of the possible expressions are acceted inside this syntax: boolean operators are not
allowed.
Vector;
[00000140] <>
Vector.new();
[00000146] <>
Vector.new(1.1);
[00000147] <1.1>
Vector.new(1.1, 2.2, 3.3);
[00000155] <1.1, 2.2, 3.3>
Vector.new("123");
[00000174:error] !!! unexpected "123", expected a Float
Vector.new([]);
[00000187] <>
Vector.new([1, 2, 3, 4, 5]);
[00000189] <1, 2, 3, 4, 5>
Vector.new(["123"]);
[00000193:error] !!! unexpected "123", expected a Float
<1, 1>;
[00000147] <1, 1>
var x = 1|;
<1+1, x+2>;
[00000000] <2, 3>
- ’*’(that)
A Vector whose members are member-wise products of this and that. The argument that must
either be a Vector of same dimension, or a Float.
Vector * Vector == Vector;
Vector.new(1, 2, 3) * Vector.new(5, 6, 7) == Vector.new(5, 12, 21);
Vector.new(1, 2, 3) * 3 == Vector.new(3, 6, 9);
Vector.new(1, 2) * Vector.new(0);
[00000601:error] !!! *: incompatible vector sizes: 2, 1
- ’+’(that)
A Vector whose members are member-wise sums of this and that. The argument that must
either be a Vector of same dimension, or a Float.
Vector + Vector == Vector;
<1, 2, 3> + <5, 6, 7> == <6, 8, 10>;
<1, 2, 3> + 3 == <4, 5, 6>;
<1, 2> + <0>;
[00000601:error] !!! +: incompatible vector sizes: 2, 1
- ’-’(that)
A Vector whose members are member-wise subtractions of this and that. The argument that
must either be a Vector of same dimension, or a Float.
Vector - Vector == Vector;
<1, 2, 3> - <5, 6, 7> == <-4, -4, -4>;
<1, 2, 3> - 3 == <-2, -1, 0>;
Vector.new(1, 2) - Vector.new(0);
[00000601:error] !!! -: incompatible vector sizes: 2, 1
- ’/’(that)
A Vector whose members are member-wise divisions of this and that. The argument that must
either be a Vector of same dimension, or a Float.
Vector / Vector == Vector;
<6, 4, 1> / <3, 2, 1> == <2, 2, 1>;
<9, 6, 3> / 3 == <3, 2, 1>;
<1, 2> / <1>;
[00000601:error] !!! /: incompatible vector sizes: 2, 1
- ’<’(that)
Whether this is less than the that Vector. This is the lexicographic comparison: this is “less
than” that if, from left to right, one of its member is “less than” the corresponding member of
that:
<0, 0, 0> < <0, 0, 1>;
!(<0, 0, 1> < <0, 0, 0>);
<0, 1, 2> < <0, 2, 1>;
!(<0, 2, 1> < <0, 1, 2>);
<1, 1, 1> < <2>;
!(<2> < <1, 1, 1>);
!(<0, 1, 2> < <0, 1, 2>);
or that 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 Vector derives from 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>;
- ’==’(that)
Whether this and that have the same size, and members.
<> == <>;
<1, 2, 3> == <1, 2, 3>;
!(<1, 2, 3> == <1, 2>);
!(<1, 2, 3> == <3, 2, 1>);
- ’[]’(index)
The value stored at index.
var v = <1, 2, 3>;
v[ 0] == 1; v[ 1] == 2; v[ 2] == 3;
v[-3] == 1; v[-2] == 2; v[-1] == 3;
v[4];
[00000100:error] !!! []: invalid index: 4
- ’[]=’(index, value)
Set the value stored at index to value and return it.
var v = <0, 0, 0>;
(v[ 0] = 1) == 1; (v[ 1] = 2) == 2; (v[ 2] = 3) == 3;
v[ 0] == 1; v[ 1] == 2; v[ 2] == 3;
(v[-3] = 4) == 4; (v[-2] = 5) == 5; (v[-1] = 6) == 6;
v[-3] == 4; v[-2] == 5; v[-1] == 6;
v[4] = 7;
[00000100:error] !!! []=: invalid index: 4
- asString
<>.asString() == "<>";
<1, 2>.asString() == "<1, 2>";
<1, 2, 3.4>.asString() == "<1, 2, 3.4>";
- asList
Return this as a list.
<>.asList() == [];
<1,-1>.asList() == [1, -1];
- asVector
Return this.
var v = Vector.new();
v.asVector() === v;
- combAdd(that)
A Matrix res such that res[i, j] = this[i] + that[j], for all i in the range of this, and j
in the range of that.
<0, 1, 2>.combAdd(<0, 10, 20>)
== Matrix.new([ 0, 10, 20],
[ 1, 11, 21],
[ 2, 12, 22]);
- combDiv(that)
A Matrix res such that res[i, j] = this[i] / that[j], for all i in the range of this, and j
in the range of that.
<10, 20>.combDiv(<2, 5, 10>)
== Matrix.new([ 5, 2, 1],
[ 10, 4, 2]);
- combMul(that)
A Matrix res such that res[i, j] = this[i] * that[j], for all i in the range of this, and j
in the range of that.
<1, 2, 3, 4>.combMul(<5, 6, 7>)
== Matrix.new([ 5, 6, 7],
[10, 12, 14],
[15, 18, 21],
[20, 24, 28]);
- combSub(that)
A Matrix res such that res[i, j] = this[i] - that[j], for all i in the range of this, and j
in the range of that.
<-1, 0, +1>.combSub(<-2, 0, +2>)
== Matrix.new([1, -1, -3],
[2, 0, -2],
[3, 1, -1]);
- distance(that)
The (Euclidean) distance between this and that: (Euclidean) norm of this - that. They must
have equal dimensions.
<>.distance(<>) == 0;
<0, 0>.distance(<3, 4>) == 5;
<3, 4>.distance(<0, 0>) == 5;
<0>.distance(<1, 2>);
[00000319:error] !!! distance: incompatible vector sizes: 1, 2
<1, 2>.distance(<0>);
[00000334:error] !!! distance: incompatible vector sizes: 2, 1
- norm
The (Euclidean) norm of this: square root of the sum of the square of the members.
<>.norm == 0;
<0>.norm == 0;
<1>.norm == 1;
<1, 1>.norm == 2.sqrt();
<0, 0, 0, 0>.norm == 0;
<1, 1, 1, 1>.norm == 2;
- resize(dim)
Change the dimensions of this, using 0 for possibly new members. Return this.
// Check that <1, 2> is equal to the Vector composed of values,
// when resized the number of values.
function resized(var values[])
{
var m = <1, 2>;
var res = Vector.new(values);
// Resize returns this...
m.resize(res.size) === m;
// ...and does resize.
m == res;
}|;
assert
{
resized();
resized(1);
resized(1, 2);
resized(1, 2, 0);
};
- scalarEQ(that)
A Vector whose members are 0 or 1 depending on the equality of the corresponding members of
this and that.
<1, 2, 3>.scalarEQ(<3, 2, 1>) == <0, 1, 0>;
this and that must have equal sizes.
<1, 2>.scalarEQ(<0>);
[00000601:error] !!! scalarEQ: incompatible vector sizes: 2, 1
- scalarGE(that)
A Vector whose members are 0 or 1 if the corresponding member of this is greater than or equal
to the one of that.
<1, 2, 3>.scalarGE(<3, 2, 1>) == <0, 1, 1>;
this and that must have equal sizes.
<1, 2>.scalarGE(<0>);
[00000601:error] !!! scalarGE: incompatible vector sizes: 2, 1
- scalarGT(that)
A Vector whose members are 0 or 1 if the corresponding member of this is greater than to the
one of that.
<1, 2, 3>.scalarGT(<3, 2, 1>) == <0, 0, 1>;
this and that must have equal sizes.
<1, 2>.scalarGT(<0>);
[00000601:error] !!! scalarGT: incompatible vector sizes: 2, 1
- scalarLE(that)
A Vector whose members are 0 or 1 if the corresponding member of this is less than or equal to
the one of that.
<1, 2, 3>.scalarLE(<3, 2, 1>) == <1, 1, 0>;
this and that must have equal sizes.
<1, 2>.scalarLE(<0>);
[00000601:error] !!! scalarLE: incompatible vector sizes: 2, 1
- scalarLT(that)
A Vector whose members are 0 or 1 if the corresponding member of this is less than to the one
of that.
<1, 2, 3>.scalarLT(<3, 2, 1>) == <1, 0, 0>;
this and that must have equal sizes.
<1, 2>.scalarLT(<0>);
[00000601:error] !!! scalarLT: incompatible vector sizes: 2, 1
- set(list)
Change the members of this, and return this.
var v = <1, 2, 3>;
v.set([10, 20]) === v;
v == <10, 20>;
- size
The size of the vector.
<>.size == 0;
<0>.size == 1;
Vector.new(0, 1, 2, 3) .size == 4;
Vector.new([0, 1, 2, 3]).size == 4;
- sum
The sum of the values stored in this.
<> .sum() == 0;
<1> .sum() == 1;
<0, 1, 2, 3> .sum() == 6;
- trueIndexes
A Vector whose values are the indexes of this whose value is non-null.
<>.trueIndexes() == <>;
<1, 0, 1, 0, 0, 1>.trueIndexes() == <0, 2, 5>;
- zip(others)
Zip this vector with the given list of other vectors, which can be of different sizes.
<0,3>.zip([<1, 4>, <2, 5>]) == <0, 1, 2, 3, 4, 5>
- serialize(wordSize, littleEndian)
Serialize to binary integral data.
<1, 2>.serialize(2, false) == "\x01\x00\x02\x00";
<1, 2>.serialize(2, true) == "\x00\x01\x00\x02";
- type
"Vector".
Vector.type == "Vector";
<>.type == "Vector";
21.81 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, 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. Actually there is a temporary exception:
nil.isVoid.
void.isVoid;
void.acceptVoid().isVoid;
! 123.isVoid;
nil.isVoid;
[ Logger ] nil.isVoid will return false eventually, adjust your code.
[ Logger ] For instance replace InputStream loops from
[ Logger ] while (!(x = i.get().acceptVoid()).isVoid())
[ Logger ] cout << x;
[ Logger ] to
[ Logger ] while (!(x = i.get()).isNil())
[ Logger ] cout << x;
- 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