git @ Cat's Eye Technologies Fountain / master doc / Tests-for-fountain.md
master

Tree @master (Download .tar.gz)

Tests-for-fountain.md @master — view markup · raw · history · blame

Tests for fountain

These tests are specific to the reference implementation of Fountain, fountain, and make reference to implementation details. They are thus not part of the main test suite.

-> Functionality "Load Fountain Grammar" is implemented by
-> shell command "bin/fountain load %(test-body-file)"

-> Functionality "Preprocess Fountain Grammar" is implemented by
-> shell command "bin/fountain preprocess %(test-body-file)"

-> Functionality "Generate using Fountain Grammar with random choices" is implemented by
-> shell command "bin/fountain --seed 145 generate %(test-body-file) %(test-input-text)"

Loading

-> Tests for functionality "Load Fountain Grammar"

Sequence.

Goal ::= "f" "o" "o";
===> Goal ::= (("f" "o" "o"));

Multi-character terminals.

Goal ::= "foo" "bar";
===> Goal ::= (("f" "o" "o" "b" "a" "r"));

Alternation and recursion.

Goal ::= "(" Goal ")" | "0";
===> Goal ::= (("(" Goal ")") | ("0"));

Repetition.

Goal ::= "(" {"0"} ")";
===> Goal ::= (("(" {(("0"))} ")"));

Constraints.

Goal ::=
     <. a = 0 .> { "a" <. a += 1 .> } <. a = n .>
     <. b = 0 .> { "b" <. b += 1 .> } <. b = n .>
     <. c = 0 .> { "c" <. c += 1 .> } <. c = n .>
     ;
===> Goal ::= ((<. a = 0 .> {(("a" <. a += 1 .>))} <. a = n .> <. b = 0 .> {(("b" <. b += 1 .>))} <. b = n .> <. c = 0 .> {(("c" <. c += 1 .>))} <. c = n .>));

Parameters and multiple productions.

Goal ::= "Hi" Sp<a> "there" Sp<b> "world" "!";
Sp<n> ::= <. n = 0 .> { " " <. n += 1 .> } <. n > 0 .>;
===> Goal ::= (("H" "i" Sp<a> "t" "h" "e" "r" "e" Sp<b> "w" "o" "r" "l" "d" "!"));
===> Sp<n> ::= ((<. n = 0 .> {((" " <. n += 1 .>))} <. n > 0 .>));

Comments.

// This is my grammar.
// A comment may come before it.

Goal ::= "foo";  // You see
// how it is
A ::= "f"  // You see
"o" // how
//
// There are many ways to place comments.
"//";
===> Goal ::= (("f" "o" "o"));
===> A ::= (("f" "o" "/" "/"));

Misplaced semicolon is a syntax error.

Goal ::= <. x = 0 .> <. a = 0 .> { DogMove<x, d> <. a += 1 .> } <. a = 1000 .>;
DogMove<x, d> ::= <. d > 0 .> "The dog ran away." #10; <. x += 1 .>
                | <. d < 0 .> "The dog came back." #10; <. x -= 1 .>
                ;
???> unexpected

Preprocessing

-> Tests for functionality "Preprocess Fountain Grammar"

Sequence.

Goal ::= "f" "o" "o";
===> Goal ::= ("f" "o" "o");

Alternation and recursion.

Goal ::= "(" Goal ")" | "0";
===> Goal ::= (("(" Goal ")") | ("0"));

Repetition.

This shows internal structure after converting loops to recursion.

Goal ::= "(" {"0"} ")";
===> Goal ::= ("(" WASALOOPHERE0 ")");
===> WASALOOPHERE0 ::= ((<. ~generating && ?("0") || generating && ~T .> ("0") WASALOOPHERE0) | (<. ~generating && ~?("0") || generating && T .>));

Goal ::=
     <. a = 0 .> { "a" <. a += 1 .> } <. a = n .>
     <. b = 0 .> { "b" <. b += 1 .> } <. b = n .>
     <. c = 0 .> { "c" <. c += 1 .> } <. c = n .>
     ;
===> Goal ::= (<. a = 0 .> WASALOOPHERE0<a> <. a = n && b = 0 .> WASALOOPHERE1<b> <. b = n && c = 0 .> WASALOOPHERE2<c> <. c = n .>);
===> WASALOOPHERE2<c> ::= ((<. ~generating && ?("c" <. c += 1 .>) || generating && ~T .> ("c" <. c += 1 .>) WASALOOPHERE2<c>) | (<. ~generating && ~?("c" <. c += 1 .>) || generating && T .>));
===> WASALOOPHERE1<b> ::= ((<. ~generating && ?("b" <. b += 1 .>) || generating && ~T .> ("b" <. b += 1 .>) WASALOOPHERE1<b>) | (<. ~generating && ~?("b" <. b += 1 .>) || generating && T .>));
===> WASALOOPHERE0<a> ::= ((<. ~generating && ?("a" <. a += 1 .>) || generating && ~T .> ("a" <. a += 1 .>) WASALOOPHERE0<a>) | (<. ~generating && ~?("a" <. a += 1 .>) || generating && T .>));

Coalescing constraints.

This shows internal structure after converting loops to recursion.

Goal ::= <. a = 0 .> <. b = 0 .> { "a" <. a += 1 .> } <. a > b .>;
===> Goal ::= (<. a = 0 && b = 0 .> WASALOOPHERE0<a> <. a > b .>);
===> WASALOOPHERE0<a> ::= ((<. ~generating && ?("a" <. a += 1 .>) || generating && ~T .> ("a" <. a += 1 .>) WASALOOPHERE0<a>) | (<. ~generating && ~?("a" <. a += 1 .>) || generating && T .>));

Nondeterminism

-> Tests for functionality "Generate using Fountain Grammar with random choices"

The method used for nondeterministic backtracking is not defined at the language level.

But the reference implementation of Fountain implements nondeterministic backtracking by ordering the choices pseudo-randomly, based on a PRNG with a particular seed, which can be chosen by the user, and processing the choices in that order.

FIXME: since this test relies on a particular PRNG implementation (whatever is bundled with ghc 9.2.8) it is not expected to pass on other implementations. Falderal should handle this gracefully, but doesn't. So this is temporarily disabled.

>   Goal<n>       ::= <. a = 0 .> Item<a, n>;
>   Item<a, n>(*) ::= <. a = n .>
>                   | "####" <. a += 4 .> <. a <= n .> Item<a, n>
>                   | "ooooo" <. a += 5 .> <. a <= n .> Item<a, n>
>                   | "xxxxxxx" <. a += 7 .> <. a <= n .> Item<a, n>;
>   <=== n=30
>   ===> ####oooooxxxxxxxoooooooooo####

Fuel Consumption During Parsing

-> Functionality "Parse using Fountain Grammar with fuel constraints" is implemented by
-> shell command "echo -n '((*))' | bin/fountain --dump-state %(test-input-text) parse %(test-body-file) -- n=2"

-> Tests for functionality "Parse using Fountain Grammar with fuel constraints"

Fuel factor 0 won't ever be enough to sustain anything.

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --initial-fuel 0 --fuel-factor 0
???> Out of fuel

But if you have enough fuel initially, it doesn't have to be sustainable.

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --initial-fuel 4 --fuel-factor 0
===> Right (Parsing {input = "", store = fromList [(Var "a",2),(Var "n",2)], fuel = FuelState {currentFuel = 1, fuelFactor = Just 0}})

Fuel factor 1 is sufficient for this parsing problem.

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --initial-fuel 1 --fuel-factor 1
===> Right (Parsing {input = "", store = fromList [(Var "a",2),(Var "n",2)], fuel = FuelState {currentFuel = 3, fuelFactor = Just 1}})

If you can do something with fuel factor 1, you can do it with fuel factor 2 as well.

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --initial-fuel 1 --fuel-factor 2
===> Right (Parsing {input = "", store = fromList [(Var "a",2),(Var "n",2)], fuel = FuelState {currentFuel = 8, fuelFactor = Just 2}})

Fuel Consumption During Generation

-> Functionality "Generate using Fountain Grammar with fuel constraints, n=3" is implemented by
-> shell command "bin/fountain %(test-input-text) generate %(test-body-file) n=3"

-> Tests for functionality "Generate using Fountain Grammar with fuel constraints, n=3"

Given enough fuel, it does generate.

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --fuel-factor 1 --initial-fuel 4
===> (((*)))

It does need some initial fuel.

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --fuel-factor 1 --initial-fuel 0
???> Failure

-> Functionality "Generate using Fountain Grammar with fuel constraints, n=10" is implemented by
-> shell command "bin/fountain %(test-input-text) generate %(test-body-file) n=10"

-> Tests for functionality "Generate using Fountain Grammar with fuel constraints, n=10"

Fuel gain, and usage, is proportional to the given n in this case.

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --fuel-factor 1 --initial-fuel 4
===> ((((((((((*))))))))))

If fuel factor is 0 it is reliant on the initial fuel.

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --fuel-factor 0 --initial-fuel 4
???> Failure

Goal<n>       ::= <. a = 0 .> Item<a, n>;
Item<a, n>(*) ::= <. a = n .> "*" | <. a < n .> "(" <. a += 1 .> Item<a, n> ")";
<=== --fuel-factor 0 --initial-fuel 15
===> ((((((((((*))))))))))