git @ Cat's Eye Technologies Unlikely / master tests / Unlikely.md
master

Tree @master (Download .tar.gz)

Unlikely.md @masterview 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!