Functions, Classes, Threads and EvalBy James Jianbo Huang October 2001 UPDATED: July 2003 non-printer versionAbstract
This article discusses user functions, classes, threads and dynamic
evaluation. Variable scope is an important topic and many things are
cleared here. JudoScript supports lambda functions, or anonymous functions,
and function variables; these are useful for, say, array sorting and
filtering. Also supported is aliasing for Java class static methods.
System functions are generally discussed. JudoScript threads are declared
like functions and started with start thread . Dynamically
created code can be evaluated either locally with eval or
separately with evalExternal and evalFile .
The syntax for function declaration is:
function fxn_name [ ( ]
params [ ) ] { statements }
The parentheses around parameters are not required. Functions always return
a value, either a null or a value issued in a return statement.
Parameter names are the same as variable names. A function is invoked by its
name with parameter values. The following is the famous Hanoi Tower problem
followed by the result of a test run. Note that functions can be declared
anywhere, because a script is parsed and all declarations are collected
before any runs.
Listing 1. hanoi.judo |
1: cnt = 0;
2: hanoiTower(4, 'A', 'B', 'C');
3:
4: function hanoiTower N, src, aux, dst {
5: if N == 0 { return; }
6: hanoiTower(N-1, src, dst, aux);
7: println 'step ', ++cnt : > 2, ': ', src, ' => ', dst;
8: hanoiTower(N-1, aux, src, dst);
9: }
|
step 1: A => B
step 2: A => C
step 3: B => C
step 4: A => B
step 5: C => A
step 6: C => B
step 7: A => B
step 8: A => C
step 9: B => C
step 10: B => A
step 11: C => A
step 12: B => C
step 13: A => B
step 14: A => C
step 15: B => C
Function parameters may take default values. In fact, a function name
is unique, and functions can be called with any number of parameters
regardless of those specified in the declaration. If fewer parameter
values are supplied than as declared, the default values are used; if
not default values, null is used. What happens if more
values than declared parameters? Those extra values are stored in an
internal array variable, $$args . To explicitly declare that
a function takes variable number of parameters, you may end the
parameter list with two dots (".."):
function inc x, delta=1, .. {
ret = x + delta;
if $$args != null {
for x in $$args { ret += x; }
}
return ret;
}
println inc(2);
println inc(2,2);
println inc(2,2,2);
function sum .. {
ret = 0;
for x in $$args { ret += x; }
return ret;
}
println sum(1,2,3,4,5,6,7,8,9);
function oneparam a {
println 'param: ', a;
if $$args != null {
println $$args.length, ' useless parameters.';
}
}
oneparam('abc');
oneparam('abc', x, y, z);
The dot command is a shortcut for println , real short.
A function variable is a variable that references a function. A function
reference is obtained by the & operator. They can be assigned
to variables or passed as paramter values to other functions.
function foo x, y { return x+y; }
a = &foo;
println a->(1,2);
Listing 2. fxn_var.judo |
1: function foo1 a, b { return a + b; }
2: function foo2 a, b { return a * b; }
3: function whichFoo f, a, b { return f->(a,b); }
4:
5: println 'whichFoo(&foo1,2,3) = ', whichFoo(&foo1,2,3);
6: println 'whichFoo(&foo2,2,3) = ', whichFoo(&foo2,2,3);
|
JudoScript supports array sorting, filtering and element-wise
conversion. They all take function variables as parameters. For instance,
array sorting take a function that should take two parameters and return
1, 0 or -1 as the result of comparison.
Listing 3. custom_sort.judo |
1: a = [ '1.2', '3', '3.9', '1.10', '1.2.1', '2.3', '3' ];
2: a.sort( &my_comparator );
3: for i from 0 to a.lastIndex() { println i, ' ', a[i]; }
4:
5: function my_comparator(lhs, rhs) {
6: la = lhs.csv('.');
7: ra = rhs.csv('.');
8: for i from 0 to la.size() {
9: if la[i].int() < ra[i].int() { return -1; }
10: if la[i].int() > ra[i].int() { return 1; }
11: }
12: if la.size() == ra.size() { return 0; }
13: return la.size() > ra.size();
14: }
|
Lines 5 through 14 defines a custom comparator. On line 1, we have an array
of something that looks like book sections. Normal string comparison fails
to yield correct order. Our comparator takes apart the section number to an
array (lines 6 and 7), and compares each parts. The result is:
0 1.2
1 1.2.1
2 1.10
3 2.3
4 3
5 3
6 3.9
With function variables, functions do not always need names. Anonymous
functions are called lambda functions.
a = [ 'a', 'ab', 'abc', 'abcd', 'abcde', 'abcdef', 'abcdefg' ];
f = lambda elem { return elem.length() >= 5; };
a.filter(f,true);
println a;
In the filter() call, the second parameter of true
indicates the filtering is done locally. The first parameter is a lambda
as a filter function.
Java class static methods can be aliased to appear like regular JudoScript
functions.
function currentTimeMillis for javaclass java.lang.System.currentTimeMillis();
function loadLibrary for javaclass java.lang.System.loadLibrary();
millis = currentTimeMillis();
loadLibrary("native.dll");
Review Questions
- How to declare a function?
- Can functions call themselves?
- If a function is invoked with more parameters than declared, how to
get the extra ones?
- If a function is invoked with less parameters than declared, what
happens?
- How to assign a function to a variable? How to invoke that function
variable? Can it be passed to a function?
- How to create an anonymous function?
- What is a comparator function? What is its requirements?
What is a filter function? Can you guess its requirements?
- How to declare a function to be an alias to a Java class static method?
»»» Top | This Section «««
JudoScript has a number of system functions for various purposes. They are
actually the methods of an internal $$sys object but
$$sys is not required to use them. This is called method
shortcut. In addition to $$sys , shortcut methods also exists
for these internal objects: $$con (database connection) and
$$timer (scheduler timer). In JudoScript, values and objects all
have methods. The mathematical functions, for instance, are methods
of numeric values. System functions (or, rather, methods), are those
that do not belong to any objects but useful system-wide.
The system functions fall into the following categories: values and
numbers, system properties and related, system controls, system
standard input-output, file input-output and generic.
See language specification
for a complete listing. These functions are discussed in detail in
respective topics; they are briefly listed below.
Value and number functions:
date [ year [ , month [ , day
[ , hour [ , minute
[ , second ] ] ] ] ] ]
time hour [ , minute [ , second ] ]
timeToday hour [ , minute [ , second ] ]
rand|random [ lower , ] upper
unit count, singular [ , plural ]
compare left, right
System properties and related functions:
systemProperty name [ , defaultVal ]
systemProperties
osName
isMac
isWindows
isHPUX
isLinux
isSunOS
javaVendor
userName
homeDir
curDir|pwd
System control functions:
exit [ returnVal ]
sleep [ duration ]
lock lockname
unlock lockname
waitFor signalname
notify signalname
notifyAll signalname
System standard input-output:
getOut
getErr
getLog
setOut printwriter
setErr printwriter
setLog printwriter
File input-output functions:
openFile filename [ , mode ]
openTextFile filename [ , mode ]
openRandomAccessFile filename [ , mode ]
openGZippedFile filename [ , mode ]
openGZippedTextFile filename [ , mode ]
openZip zipfilename
createZip zipfilename
createJar zipfilename [ , manifest ]
getFileContent filename
getGZippedFileContent gzipfilename
Generic functions:
setHttpProxy server , port
timerHandler
setGuiListener component , eventhandlername
connectMailServer server
[ , username [ , password ] ]
disconnectMailServer
getInitialContext|initialContext factoryname , url
[ , username [ , password ] ]
getWeblogicContext|weblogicContext url
[ , username [ , password ] ]
Review Questions
- What is "method shortcut"?
- How to easily print out "I have 1 apple." or "I have 3 apples"?
- How to get the home directory?
- How to save logged information to a file of your choice?
- How to platform-specific tasks, such as loading native libraries?
»»» Top | This Section «««
JudoScript supports user-defined classes. A JudoScript class is just a struct with
user-defined methods. All struct operations are available to user-defined
classes. See the other article for how
to use structs. All the members (or attributes) are accessible; there
is no accesss control such as "private" or "protected". A class instance
is initialized just like an Object except the class name is used instead
of "Object":
a = new MyClass( alfa=1, beta='abc' );
println a.alfa, ' ', a.beta;
Classes can have constructors; they do not take any parameters, and are invoked
after the object is initialized. Because data members are not declared, it is
good practice to document them in the comment preceding or withing the class.
The following example defines a Node class and constructs a tree for traversals.
Listing 4. dfs_bfs.judo |
1: class Node
2: {
3: constructor {
4: assert(name != null); // mandates that 'name' is initialized
5: children = [];
6: }
7: function addChild child { children.add(child); }
8: function toString { return name; }
9: }
10:
11: // constuct a tree
12: root = new Node(name='ROOT');
13: a = new Node( name='A' );
14: a.addChild(new Node(name='A1'));
15: a.addChild(new Node(name='A2'));
16: root.addChild(a); // left subtree
17: a = new Node( name='B');
18: a.addChild(new Node(name='B1'));
19: a.addChild(new Node(name='B2'));
20: a.addChild(new Node(name='B3'));
21: root.addChild(a); // right subtree
22:
23: dfs(root);
24: bfs(root);
25:
26: function dfs root {
27: print 'Depth-first traverse: ';
28: $stack = new stack;
29: $stack.push(root);
30: while !$stack.isEmpty() {
31: node = $stack.pop();
32: print node, ' ';
33: for x in node.children backward { $stack.push(x); }
34: }
35: println;
36: }
37:
38: function bfs root {
39: print 'Breadth-first traverse: ';
40: $queue = new queue;
41: $queue.enq(root);
42: while !$queue.isEmpty() {
43: node = $queue.deq();
44: print node, ' ';
45: for x in $node.children { $queue.enq(x); }
46: }
47: println;
48: }
|
Class Node has two attributes: "name" and "children". In the
constructor, lines 4 mandates that an attributed called "name" must be
initialized, and line 5 creates the attribute "children" as an array. The
class defines a method calld addChild . It also defines a
toString() method; when this is defined, the println
statement prints whatever it returns.
A class can extend another. For methods, since parameters have no types
and any number of parameters are allowed, each method must be unique.
When a class is extended, that method may be overridden. To invoke a
parent's method. use the super qualifier.
Listing 5. john_son.judo |
1: class John
2: {
3: constructor {
4: println "I love my baseball club.";
5: }
6:
7: function plays {
8: return new array( 'baseball' );
9: }
10: }
11:
12: class Johnson extends John
13: {
14: constructor {
15: println "I always bring my tennis racket.";
16: }
17:
18: function plays {
19: arr = super.plays();
20: arr.add('basketball', 'tennis', 'soccer', 'gymnastics', 'piano' );
21: return arr;
22: }
23: }
24:
25: println (new Johnson).plays();
|
The method plays() is overridden in the child class; it calls
the parent's version to get an array of what his dad is up to, addes
his own and returns the array. The constructors of both classes print
out a message. The parent classes' constructors are run first. The
result is:
I love my baseball bat.
I always bring my tennis racket.
[baseball,basketball,tennis,soccer,gymnastics,piano]
In the methods, own methods can be invoked without any qualifiers; in
other words, own method names take precedence over global function
names. To access own's own attributes, use get() method or
the this.() notion.
Classes without a parent conceptually extend struct. You can make
a class extend ordered map.
class MyClass extends orderedMap
{
constructor { alfa=1, beta=2, gamma=3 }
function printAll {
for x in keys() { println x, ' => ', this.(x); }
}
}
Instances of a class can be converted to another class on-the-fly by the
built-in method transpose() . This is typically used when a struct,
orderedMap or class is returned from some functions and methods (especially
the built-in ones), and later a new class with more and newer methods is
intended to act upon it. As we know, a class is nothing but a collection of
dynamic data members, transposing a class to another is essentially
re-associating the data with a new set of methods, except that structs can
not be cast to orderedMaps because the latter has mechanisms that the
former lacks.
The following program calls the string method parseUrl() which
returns a struct of various information; on lines 26 through 34, a custom
class, UrlInfo , is defined with a toString() method
(lines 28 through 33) that reconstructs the URL. It is used on line 23 to
convert the struct into a UrlInfo, and calles its toString()
method implicitly.
Listing 6. parse_url.judo |
1: a = ['http://localhost:8080/articles/developer.html#top',
2: 'http://localhost/cgi-bin/check.pl?id=1234&sess=abc',
3: '/index.html',
4: 'index.html',
5: '',
6: '/',
7: 'http://www.judoscript.com',
8: 'images/bg.gif'
9: ];
10: for url in a {
11: b = url.parseUrl();
12: println '-----------------------------------------------';
13: println ' URL: ', url;
14: println ' root: ', b.root;
15: println ' protocol: ', b.protocol;
16: println ' domain: ', b.domain;
17: println ' host: ', b.host;
18: println ' port: ', b.port;
19: println ' path: ', b.path;
20: println ' file_name: ', b.file_name;
21: println ' query_string: ', b.query_string;
22: println ' ref: ', b.ref;
23: println 'constructed URL: ', b.transpose('UrlInfo');
24: }
25:
26: class UrlInfo
27: {
28: function toString() {
29: ret = root @ path;
30: if query_string != null { ret @= '?' @ query_string; }
31: if ref != null { ret @= '#' @ ref; }
32: return ret;
33: }
34: }
|
Review Questions
- Define a class called
Order that have an attribute
"createTime" defaulted to now and "amount" defaulted to 0.
- The
Order class requires attribute "name" be initialized
to a non-empty string. How to enforce this?
- A class is just a struct. User can attach more and more attributes.
How to list all the attributes at a particular moment?
- When a class is extended, when is the parent class's constructor called?
- The
this reference is avaiable in a method.
Can you think of its uses?
- An object is of a user-defined class which extends
orderedMap .
When it is being converted to another class which extends struct
via a transpose() call, what happens? And vice versa?
»»» Top | This Section «««
First of all, let us clear some terminology. A function in the global scope
is called a function; a function defined within a class is called a method.
A pair of curly braces that enclose 0 or more statements is called block;
blocks can be enclosed in other blocks. The curly braces for a class does
not constitute a block because they never enclose statements. A variable
name starts with a dollar sign; a name without a leading dollar sign is a
class data member (or attribute).
A context is where variables reside. When a program starts, it is in a root
or global context. In most cases, a block is equivalent to a context, for
instance, a function, a thread, a loop statement like for and
while , a try-catcn-finally block, and anonymous blocks. The only
exceptions are the blocks in if-else and switch
statements, because such simple braching does not deserve a separate
context yet syntactically they do have blocks.
When a variable is referenced, the JudoScript engine starts searching from the
current context up the block chain, until either the variable is found in
one of the context, or a "terminal" context is reached, in which case a
null is returned. A terminal context is that of a method or the
global context itself. Global functions are considered child contexts of
the global one. You can force to access a global variable (variables in
the global context) from any code by preceding the variable name with the
:: decorator. Constants are always global.
When a variable is being assigned a value, the same search process
occurs; if found, the value is set for the variable in that context. If
not, a new variable is created in the current context. A local variable
can be created in the current context by the decorator local in
front of the assignment, so that an variables with the same name in
parent contexts are not accessed and affected.
Class members are, strictly speaking, not variables. They can be
referenced and assigned values, and are always in the class instance.
The following is a boring example that simply experiments with scopes:
Listing 7. var_scope.judo |
1: println "global a = ", a = 1;
2: {
3: local a = 2;
4: println "local a = ", a;
5: println "::a = ", ::a;
6: }
7: println 'global a = ', a;
8:
9: class MyCls
10: {
11: function foo {
12: println "in class: a = ", a;
13: local a = 2;
14: println "in class: local a = ", a;
15: println "in class: ::a = ", ::a;
16: }
17: }
18: (new MyCls).foo();
|
The result is:
global a = 1
local a = 2
::a = 1
global a = 1
in class: a =
in class: local a = 2
in class: ::a = 1
Review Questions
- What is the difference between a function and a method in terms of
variable scope?
- When a variable is not defined anywhere yet, where will it be created
by an assignment?
- What will be printed in the following program? If the first line is
uncommented, then what?
//x = 9;
for x from 0 to 5 {
}
println x;
- In the following
for x in array {}
is x local? (Judge by common sense.)
- How to defined a local variable? Why you may need to do so?
- How to access a global variable and constant from a class method?
»»» Top | This Section «««
A JudoScript thread is defined exactly the same as a function except the keyword
is thread instead of function . It can take parameters
optionally with default values. Threads do not, however, return any values.
Threads are global objects; they can not be declared within a class. To
start a thread, use the start thread or start daemon thread
command.
thread myFirstThread millis {
for i from 1 to 10 {
sleep(millis);
println "Hah!";
}
}
start thread myFirstThread(1000);
Or you can start an anonymous thread without declaration:
start thread millis = 1000 {
for i from 1 to 10 {
sleep(millis);
println "Hah!";
}
}
The parameters in this case are for code clean-ness. They should all be
initialized.
Threads are run in their own contexts, a child to the root context. The
parameters are evaluated in the current context and their values are
stored in the new thread context.
Threads run with others; coordination of accesses to variables is essential
to thread programming. There are two ways to share variables among threads:
global variables and non-primitive parameters. If two threads happen to
modify a same shared object at the same time, the result will be unpredicted.
To ensure ownership of a shared object, use system functions lock()
and unlock() to serialize accesses.
Listing 8. locks.judo |
1: const #fmt = 'mm:ss.SSS'; // minute:second.millisecond
2:
3: thread t name, dur = 2000 {
4: while {
5: sleep(dur);
6: lock 'house';
7: println name, ' have it! ', date().fmtDate(#fmt);
8: sleep(dur);
9: println name, ' give up. ', date().fmtDate(#fmt);
10: unlock 'house';
11: }
12: }
13:
14:
15: start thread t('Kitty', 1000);
16: sleep(1300);
17: start thread t('Doggy');
|
In this program, a lock named "house" is used to serialize execution. When
one thread owns the lock, others trying to lock it are blocked until that
owner thread unlocks. The result is listed below; look at the time of the
events, Doggy holds the lock for 2 second and Kitty holds for 1 second.
When one releases the lock, the other grabs it immediately.
Kitty have it! 58:15.706
Kitty give up. 58:16.707
Kitty have it! 58:17.709
Kitty give up. 58:18.710
Doggy have it! 58:18.710
Doggy give up. 58:20.713
Kitty have it! 58:20.713
Kitty give up. 58:21.714
Doggy have it! 58:22.716
Doggy give up. 58:24.719
Kitty have it! 58:24.719
......
To synchronize threads, the system function waitFor() , notify()
and notifyAll() can be used. These functions work on a signal name.
The following program is a classic producer-consumer problem. The consumer
thread waits for the goods produced by the producer; if no goods available,
it go to sleep until somebody (typically the producer) wakes it up. The
producer thread periodically produces a random number of goods.
Listing 9. producer_consumer.judo |
1: inventory = 0; // shared
2:
3: thread consumer
4: {
5: while inventory <= 0 {
6: waitFor 'has-goods';
7:
8: lock 'use-goods';
9: for x from 1 to inventory { print '* '; }
10: println;
11: inventory = 0;
12: unlock 'use-goods';
13: }
14: }
15:
16: thread producer
17: {
18: while {
19: sleep 1000;
20: lock 'use-goods';
21: if inventory == 0 { inventory = random(5,10); }
22: unlock 'use-goods';
23:
24: notifyAll 'has-goods';
25: }
26: }
27:
28: start thread consumer;
29: start thread producer;
|
In the consumer thread, lines 8 and 12 guarantee ownership of the global
variable inventory , as do lines 20 and 22 in the producer thread.
The producer wakes up all the waiting consumer threads on line 24 when
the inventory is beyond 0.
Review Questions
- How to declare a thread? Can you declare it in a class?
- How to start a thread? Can you start it from a method?
- Do you have to acquire a lock to access and modify a global
variable in a thread?
- The main program runs in the default main thread. Can you
share global variables with threads? If so, is serialized
accesses necessary?
- How to synchronized threads with
waitFor() , notify()
and notifyAll() ? Can you use locks to do so?
»»» Top | This Section «««
You can dynamically create JudoScript code and run, either in the current runtime
context with eval command or in a separate context with
evalExternal and evalFile . evalFile takes a file
name instead of code.
eval '. "ABCDEFG"';
When the dynamic code is evaluated with eval , the declarations
such as functions, classes and constants are taken into the current runtime
context. evalExternal and evalFile do not.
eval [[*
const #acon = 'a';
function foo() { println 'From foo()'; }
class BarCls { constructor { println 'BarCls.ctor()'; } }
*]];
println '#acon = ', #acon;
foo();
new BarCls;
evalExternal and evalFile can take parameters using the
with clause:
code = [[*
if #args.length < 0 {
println 'Usage: java judo ', #prog, ' x';
exit 0;
}
println 'The argument is: ', #args[0];
*]];
evalExternal code;
evalExternal code with 'abcd';
The result is:
Usage: java judo x
The argument is: abcd
The following is an example. Last July, Rainbow Inc., a retail company,
ramped up its e-commerce presence. This July, Mr. Smart is hired to head
the marketing department. He devised a sophisticated pricing scheme to
promote certain products. Fortunately, the consultants who designed the
system used JudoScript's dynamic evaluation feature, so Mr. Smart's unforeseeable
request is entered into the system without any code rewrite.
Listing 10. new_princing.judo |
1: // pre-Mr.Smart pricing -- flat
2: pricing = lambda prod, quantity {
3: switch prod {
4: case 'candy': return 1.49 * quantity;
5: case 'chips': return 2.49 * quantity;
6: case 'beer': return 2.99 * quantity;
7: default: return 0.99 * quantity;
8: }
9: };
10:
11: println '10 candies: ', pricing('candy', 10);
12:
13: // post-Mr.Smart pricing -- quantity discount
14: eval [[[*
15: pricing = lambda prod, quantity {
16: switch prod {
17: case 'candy': unitprice = 1.49; break;
18: case 'chips': unitprice = 2.49; break;
19: case 'beer': unitprice = 2.99; break;
20: default: unitprice = 0.99; break;
21: }
22: if quantity <= 5 {
23: return unitprice * quantity;
24: } elif quantity <= 10 {
25: return (unitprice * 0.95 * quantity).fractionDigits(2);
26: } else {
27: return (unitprice * 0.90 * quantity).fractionDigits(2);
28: }
29: };
30: *]];
31:
32: println ' 5 candies: ', pricing('candy', 5);
33: println '10 candies: ', pricing('candy', 10);
34: println '15 candies: ', pricing('candy', 15);
|
What happens is, the software has a admin-tool that allows a human operator
to type in the new pricing scheme (lines 15 through 29) and submit to the
system, test out and commit the changes.
Review Questions
- Think of a situation where functions are dynamically created and used.
- How to run dynamically generated code without affecting the current
runtime context?
»»» Top | This Section «««
User functions and class methods are declared with a name and 0 or more
parameters, with or without default values. A function or method can be
invoked with differnt number of parameters than what is declared. When
fewer parameters present, null is used for the missing parameters;
if more parameters than declared are passed, the extra ones are in the
array $$args . Functions can be recusrively called. Functions can
be assigned to variables by the & operator. Lambda functions
are anonymous functions. Function variables are useful for array sorting,
filtering, element-wise conversion and any dynamic programming. Java class
static methods can be aliased and be used like a regular JudoScript function.
JudoScript supports a set of system functions; they are actually the methods of a
built-in object, $$sys . These functions can be called directly
via a mechanism called function shortcut. Most of the system functions are
listed in categories without much explanation.
User classes are extensions to struct or ordered map. They typically have
methods. Method toString() is special; when defined, is used by
the print statements. Classes may have a constructor, which
takes no parameters. It is called after the class object is initialized.
Use new operator to create a class instance, with named
initializers for the the class members or attributes. In the constructor,
you can ensure certain members are initialized by calling
assert(somefield != null); . Classes can singly extend others. Parent
classes' constructors are called first. In the class methods, the special
variable this to pass itself to other methods, and super
to invoke the immediate parent's same-name methods.
Variables belong to a context, normally equivalent to a block in the code;
the only exceptions are blocks of if-elif-else and switch
statements. New variables are created in the current context when there
are no variable with that name exist in the context chain. Global
functions use the global context as its parent context; class methods have
no parent context. The decorator :: in front of a variable name
explicit accesss a global variable. To explicitly define a variable in the
current context, use the local decorator; same-namevariables in
the parent contexts are shielded and not accessible except for explict
global accesses.
Threads are defined just like a function in JudoScript except the keyword
thread replaces function . They are started via the
start thread command. Anonymous threads can be started directly.
Threads are global objects. The ways to share information is either by
global variables or parameters of compound data structures. To
synchronize threads and serialize access to shared object, use the system
functions lock() , unlock() , waitFor() ,
notify() and notifyAll() .
JudoScript code can be dynamically generated and evaluated by eval
and evalExternal commands. The eval command evaluates
the code locally; declarations in the code are taken and kept.
evalExternal evaluates in a separate runtime context.
»»» Top | This Section «««
- hanoi.judo
- fxn_var.judo
- custom_sort.judo
- dfs_bfs.judo
- john_son.judo
- parse_url.judo
- var_scope.judo
- locks.judo
- producer_consumer.judo
- new_princing.judo
|