Tree @master (Download .tar.gz)
Unlikely.md @master — view markup · raw · history · blame
Tests for Unlikely
These tests are written in [Falderal][] 0.14 format. Each indented code block
(generally preceded by a description) represents a test. All the lines of
the code block up until the ===> line give the input to be tested; the
text after ===> gives the expected output. ???> gives an expected
error message, which is permitted to be a partial (substring) match.
-> Functionality "Parse Unlikely Program" is implemented by
-> shell command
-> "bin/coldwater --parse-only %(test-body-file)"
-> Functionality "Interpret Unlikely Program" is implemented by
-> shell command
-> "bin/coldwater %(test-body-file)"
Unlikely Syntax
-> Tests for functionality "Parse Unlikely Program"
Here is a syntactically correct program.
class Count(Count,Chain,Print,Add) extends Continuation
class CountForever(Count,Chain,Print,Add) extends Program {
Count c;
method continue(Passive accumulator) {
c = new Count(Passive,Count,Chain,Print,Add);
goto c.continue(new 1(Passive));
}
}
class Count() extends Continuation {
Count c;
Print p; (* this is a comment *)
Add a;
method continue(Passive accumulator) {
c = new Count(Passive,Count,Chain,Print,Add);
a = new Add(Passive,Chain);
a.value = new 1(Passive);
a.next = c;
p = new Print(Passive,Chain);
p.next = a;
goto p.continue(accumulator);
}
}
===>
And here is one which is not syntactically correct, because the parameter to the method is missing its type declaration.
class Hello(Print,Chain,Stop) extends Program {
Print p;
method continue(accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
goto p.continue(new "Hello, world!"(Passive));
}
}
???> ArtefactNotFoundError
Every method must end with a goto.
class Hello(Print,Chain,Stop) extends Program {
Print p;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
}
}
???> ArtefactNotFoundError
It is possible to declare a method with multiple parameters.
class Hello(Print,Chain,Stop) extends Program {
Print p;
Passive value;
method continue(Passive accumulator, Passive value) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
goto p.continue(new "Hello, world!"(Passive));
}
}
===>
Unlikely Semantics
-> Tests for functionality "Interpret Unlikely Program"
Rudiments
Here is a program that just prints a string and stops.
class Hello(Print,Chain,Stop) extends Program {
Print p;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
goto p.continue(new "Hello, world!"(Passive));
}
}
===> Hello, world!
If Stop receives an integer, that integer becomes the exit code
for the implementation.
class Halt(Chain,Stop) extends Program {
Passive zero;
method continue(Passive accumulator) {
zero = new 0(Passive);
zero.next = new Stop(Passive);
goto zero.continue(zero);
}
}
===>
class Halt(Chain,Stop) extends Program {
Passive one;
method continue(Passive accumulator) {
one = new 1(Passive);
one.next = new Stop(Passive);
goto one.continue(one);
}
}
???>
When continuing a method, it is possible to pass to it the value of a property which exists only on the class of the current method.
class Hello(Print,Chain,Stop) extends Program {
Print p;
Passive s;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
s = new "Hello, world!"(Passive);
goto p.continue(s);
}
}
===> Hello, world!
Add two integers.
class AddTest(Add,Chain,Print,Stop) extends Program {
Add a;
Print p;
Passive zero;
method continue(Passive accumulator) {
zero = new 0(Passive);
zero.next = new Stop(Passive);
p = new Print(Passive,Chain);
p.next = zero;
a = new Add(Passive,Chain);
a.value = new 2(Passive);
a.next = p;
goto a.continue(new 3(Passive));
}
}
===> 5
Subtract, Multiply, Divide, and Equal, chained together.
class BinOpsTest(Subtract,Multiply,Divide,Equal,Chain,Print,Stop) extends Program {
Subtract s;
Multiply m;
Divide d;
Equal e;
Print p1;
Print p2;
Print p3;
Print p4;
method continue(Passive accumulator) {
p4 = new Print(Passive,Chain);
p4.next = new Stop(Passive);
e = new Equal(Passive,Chain);
e.value = new 6(Passive);
e.next = p4;
p3 = new Print(Passive,Chain);
p3.next = e;
d = new Divide(Passive,Chain);
d.value = new 20(Passive);
d.next = p3;
p2 = new Print(Passive,Chain);
p2.next = d;
m = new Multiply(Passive,Chain);
m.value = new 3(Passive);
m.next = p2;
p1 = new Print(Passive,Chain);
p1.next = m;
s = new Subtract(Passive,Chain);
s.value = new 10(Passive);
s.next = p1;
goto s.continue(new 3(Passive));
}
}
===> 7
===> 21
===> 0
===> False
Test of greater-than.
class GreaterThanTest(GreaterThan,Chain,Print,Stop) extends Program {
GreaterThan g;
Print p;
Passive zero;
method continue(Passive accumulator) {
zero = new 0(Passive);
zero.next = new Stop(Passive);
p = new Print(Passive,Chain);
p.next = zero;
g = new GreaterThan(Passive,Chain);
g.value = new 5(Passive);
g.next = p;
goto g.continue(new 2(Passive));
}
}
===> True
If, taking the next branch.
class IfTrueTest(If,Chain,Print,Stop) extends Program {
If i;
Print p;
Passive yes;
Passive no;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
yes = new "yes"(Passive);
yes.next = p;
no = new "no"(Passive);
no.next = p;
i = new If(Passive,Chain);
i.next = yes;
i.else = no;
goto i.continue(new True(Passive));
}
}
===> yes
If, taking the else branch.
class IfFalseTest(If,Chain,Print,Stop) extends Program {
If i;
Print p;
Passive yes;
Passive no;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
yes = new "yes"(Passive);
yes.next = p;
no = new "no"(Passive);
no.next = p;
i = new If(Passive,Chain);
i.next = yes;
i.else = no;
goto i.continue(new False(Passive));
}
}
===> no
Inheritance
A program can define multiple classes. One class can instantiate another.
class Hello(Print,Stop) extends Chain {
Print p;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
goto p.continue(new "Hello, world!"(Passive));
}
}
class HelloProgram(Hello,Print,Chain,Stop) extends Program {
Hello h;
method continue(Passive accumulator) {
h = new Hello(Passive,Chain,Print,Stop);
goto h.continue(new 0(Passive));
}
}
===> Hello, world!
A user-defined class can subclass another user-defined class.
class Greeting(Print,Stop) extends Chain {
Print p;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
goto p.continue(new "Greetings!"(Passive));
}
}
class Hello() extends Greeting {
}
class HelloProgram(Hello,Print,Chain,Stop) extends Program {
Hello h;
method continue(Passive accumulator) {
h = new Hello(Passive,Chain,Print,Stop);
goto h.continue(new 0(Passive));
}
}
===> Greetings!
The subclass can override methods in the superclass, but still has access to the dependant classes declared in the superclass.
class Greeting(Print,Stop) extends Chain {
Print p;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
goto p.continue(new "Greetings!"(Passive));
}
}
class Hello() extends Greeting {
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
goto p.continue(new "Hello, world!"(Passive));
}
}
class HelloProgram(Hello,Print,Chain,Stop) extends Program {
Hello h;
method continue(Passive accumulator) {
h = new Hello(Passive,Chain,Print,Stop);
goto h.continue(new 0(Passive));
}
}
===> Hello, world!
Dependency Injection
When instantiating a class, the instantiating code can inject a different subclass to use for one or more of the dependant classes.
Here, we continue Hello, which would normally print "Hello, world!",
but we inject a (not-exactly-LSP) subclass of Print which doesn't
print at all, only stops. And we see that it produces no output.
class Hello(Print) extends Chain {
Print p;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = next;
goto p.continue(new "Hello, world!"(Passive));
}
}
class GoodbyePrint(Stop) extends Print {
Stop s;
method continue(Passive accumulator) {
s = new Stop(Passive);
goto s.continue(accumulator);
}
}
class HelloProgram(Hello,GoodbyePrint,Chain,Stop) extends Program {
Hello h;
method continue(Passive accumulator) {
h = new Hello(Passive,Chain,GoodbyePrint);
h.next = new Stop(Passive);
goto h.continue(new 0(Passive));
}
}
===>
A bit more interesting injection case, and one that exercises the dependency injection system more thoroughly.
class Hello(Print,Stop) extends Chain {
Print p;
method continue(Passive accumulator) {
p = new Print(Passive,Chain);
p.next = new Stop(Passive);
goto p.continue(new "Hello, world!"(Passive));
}
}
class PrintTwice(Print) extends Print {
Print p1;
Print p2;
method continue(Passive accumulator) {
p2 = new Print(Passive,Chain);
p2.next = next;
p1 = new Print(Passive,Chain);
p1.next = p2;
goto p1.continue(accumulator);
}
}
class HelloProgram(Hello,PrintTwice,Chain,Stop) extends Program {
Hello h;
method continue(Passive accumulator) {
h = new Hello(Passive,Chain,PrintTwice,Stop);
goto h.continue(new 0(Passive));
}
}
===> Hello, world!
===> Hello, world!