parrotcode: Untitled | |
Contents | Language Implementations | Jako |
Jako is a simple malleable language. It has very little built-in functionality, but it is very easy to build a reasonable language out of it.
The basic constructs of Jako are predefined, but the ways in which they combine are programmer-defined.
A word is a contiguous string of characters matching the Perl regular expression m/[A-Za-z][A-Za-z0-9_]*/
.
Words can be used as the names of types,
variables and constants,
and can also be used as key words.
Symbols are
A named block can be executed by passing it to eval
.
The section "A Mathematical Note" of [MEYER-2001] presents a model of a class as a set of name-value bindings.
Lesson 3, "Semantic Building Blocks" of [MEAD-2001] describes the environment (or context) of a program at a specific point in its execution in a similar way.
A block is an environment. Names not explicitly bound in that environment are implicitly bound if they are explicitly bound in an ancestor environment.
bind env E word N any V := { E.N = V; }
Parentheses are used to delimit blocklists. A parenthetical list of expressions is semantically equivalent to the same number of single-expression blocks, one after the other. That is,
(a < b, 5, i++)
is exactly the same as
{a < b} {5} {i++}
def
def goto label L := ...
Even though Jako doesn't have conventional constructs built-in, it is very easy to define them using the facilities Jako does provide:
Translating from Eiffel notation to a Perl-like notation yields something like this:
from {...} invariant (...) variant {...} until (...) loop {...} end;
where each {...}
represents a block of statements and each (...)
represents a single boolean expression.
Since the body of the loop is delimited by the braces, we can get rid of the end
keyword:
from {...} invariant (...) variant {...} until (...) loop {...};
Since we are used to using redo
to go back to the top of the loop block and re-do the current iteration, we'll replace loop
with do
:
from {...} invariant (...) variant {...} until (...) do {...};
Eiffel's variant
clause is equivalent to the Perl continue
clause:
from {...} invariant (...) continue {...} until (...) do {...};
But, we like to see that clause at the end:
from {...} invariant (...) until (...) do {...} continue {...};
Since we've adopted the Perlish continue
in place of variant
, now invariant
seems out of place, but check
seems to fit nicely:
from {...} check (...) until (...) do {...} continue {...};
With this Jako definition:
def while block W ( do? block D ( continue? block C )? )? := {
var R: typeof(D); # TODO: typeof(D) == block, we want type of *last statement of* D.
CONT: goto :LAST unless eval W;
REDO: R = eval D;
NEXT: eval C;
goto :CONT;
LAST: return R;
}
we can write Perlish while loops:
i = 0;
while (x[i] < y[i]) do {
print "$i\n";
} continue {
i++
}
In fact, we can even write very concise while
loops, given the optionality of the do
and continue
blocks and the equivalence of (x;y)
to {x} {y}
:
i = 0;
while (x[i] < y[i]; print "$i\n"; i++);
(which almost looks like a for
loop).
This Jako definition:
def for block F while? block W continue? block C do? block D := {
var R: typeof(D); # TODO: typeof(D) == block, we want type of *last statement of* D.
FOR: eval F;
CONT: goto :LAST unless eval W;
REDO: R = eval D;
NEXT: eval C;
goto :CONT;
LAST: return R;
}
allows us to write for
loops the way we are used to, such as:
for (i = 0; i < l; i++) { print x[i], "\n" }
which is really shorthand for:
for { i = 0 } { i < l } { i++ } { print x[i], "\n" }
or, more verbosely:
for {
i = 0;
} while {
i < l
} continue {
i++
} do {
print x[i], "\n"
}
We can define the familiar loop control statements given the consistency of label definitions above:
def redo ( label L )? := {
goto L:REDO; # if L not given, means "goto :REDO"
}
def next ( label L )? := {
goto L:NEXT; # if L not given, means "goto :NEXT"
}
def last ( label L )? := {
goto L:LAST; # if L not given, means "goto :LAST"
}
TODO: Allow x = next
? -- What would that mean?
def if block I then? block T ( elsif block EI then? block ET )* (else block E)? := {
var R: typeof(...);
IF: goto +:NEXT unless eval I; # "+" --> forward-only
REDO: R = eval T;
goto +:LAST;
NEXT: {
*:IF: goto :*:NEXT unless eval *EI;
*:REDO: R = eval *ET;
goto LAST;
*:NEXT:
} over elsif
REDO: R = eval E;
LAST: return R;
}
A class is an environment template. An instance is an environment built from such a template. Method application is the execution of code within the instance's environment. For example:
y = foo.x
means to find 'x' in the environment 'foo' and point 'y' in the current environment at it.
Issues: Multiple inheritance for blocks that represent classes vs. single parents for blocks that represent general code sequences.
Analogy:
closure : block :: instance : class
Instantiation as environment cloning.
The word 'self' means the enclosing environment (instance, in the case of an object method).
The word 'class' means the enclosing environment's enclosing environment.
Problem:
class {
method x {
{
self.y; # Looks in method
}
self.z; # Looks in instance
}
int y;
int z;
}
|