+
+testcase "unnamed" {
+ input "aaaaa";
+ output "top:{a q a}";
+
+ s = top:: z (q::"a"*) z
+ z = a:: "a"
+}
+
+testcase "dangling else" {
+ input "if (x) if (y) z else q";
+ output "if:{ident:{x} else:{if:{ident:{y} then:{ident:{z}}} ident:{q}}}";
+
+ s = Expr
+ Expr = if:: "if" "(" Expr ")" IfBody /ws
+ | ident:: [a-z]++
+ IfBody = else:: Expr "else" Expr /ws
+ | then:: Expr &~ (("":: (~[])*) "else" Expr! /ws)
+ ws = "":: [ ]**
+}
+
+
+testcase "unnamed" {
+ input "12111211";
+ output "ac:{{2 1 2 1}}";
+
+ s = ab:: ab
+ | ac:: ac
+ | bc:: bc
+ ab = a & b
+ ac = a & c
+ bc = b & c
+ a = "":: ("1" x)*
+ b = "":: ("b":: x "2")*
+ c = "":: ("c":: x "2" x "1")*
+ x = [123]
+}
+
+testcase "follow restrictions on empty string" {
+ input "xxx";
+ s = x:: "x" A "x" C "x"
+ A = B
+ B = "y"
+ | () -> ~"x"
+ C = D -> ~"x"
+ D = ()
+}
+
+testcase "unnamed" {
+ input "axxxxxc";
+ output "q:{a {x x x x x} c}";
+ s = q:: A ws (B|()) ws C
+ ws = "":: [x]**
+ A = a:: "a"
+ B = b:: "b"
+ C = c:: "c"
+}
+
+testcase "unnamed" {
+ input "aaaaaaaaaaaaaaaaaaaa";
+ output "";
+ s = As & AAs
+ As = () | As "a"
+ AAs = () | AAs "aa"
+}
+
+testcase "question mark" {
+ input "aa aba abba";
+ output "s:{Y Y Z}";
+ s = s:: X " " X " " X
+ X = Y > Z
+ Y = Y:: "a" B? "a"
+ Z = Z:: "a" "b"* "a"
+ B = "b"
+}
+
+testcase "operator: ... " {
+ input "aaabbbaaa abababababa";
+ output "s:{C:{a a a b b b a a a} B:{a b a b a b a b a b a}}";
+ s:: = A " " A
+ A = B > C
+ B:: = [ab]* &~ (... "bbb" ...)
+ C:: = [ab]*
+}
+
+testcase "operator: ~~" {
+ input "aaabbbaaa abababababa";
+ output "s:{C:{a a a b b b a a a} B:{a b a b a b a b a b a}}";
+ s:: = A " " A
+ A = B > C
+ B:: = ~~(... "bbb" ...)
+ C:: = [ab]*
+}
+
+testcase "lifts" {
+ input "a+(b*c)";
+ output "+:{a *:{id:{b} c}}";
+
+ s = r
+ r = id
+ | r ^"*" `r
+ | `r ^"+" r
+ | "(" r ")"
+ id:: = [a-z]++
+}
+
+testcase "epsilon as a positive conjunct" {
+ input "abababab";
+ s:: = X*
+ X:: = "a" ("b"+ & ())
+}
+
+testcase "ensure sharing of so-called reduction nodes" {
+ input "a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a ";
+ ignore output;
+ s:: = (S!)+
+ S:: = A:: "a "
+ | B:: "a "
+}
+
+testcase "epsilon as a negative conjunct" {
+ input "aaaaa";
+ s:: = X*
+ X:: = "a" ("b"* &~ ())
+}
+
+testcase "long input (reported by David Crawshaw)" {
+ input "0123456789";
+ s:: = X*
+ X:: = "a" ("b"* &~ ())
+}
+
+testcase "a case PEGs cannot handle" {
+ input "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+ s:: = X
+ X:: = "x" X "x" | "x"
+}
+
+testcase "indentation-driven binary tree parsing" {
+ input "
+a
+.g
+..b
+..b
+.q
+";
+ output "block:{interior:{{a} block:{interior:{{g} block:{leaf:{b}} block:{leaf:{b}}}} block:{leaf:{q}}}}";
+
+ s = block "\n"
+
+ // In the example below, the newline character \n is considered
+ // to be the "first" character of each line (rather than the "last"
+ // character of each line). This convention is a bit odd, but makes
+ // the example easier to understand.
+
+ // this example uses periods for indentation to make the
+ // examples easier to read; in real life you would use
+ // whitespace
+ indent = "."**
+
+ // outdent matches any sequence of lines in which the first
+ // line has indentation strictly greater than some subsequent line
+ outdent! = "." outdent "."
+ | "." ~[.] ~[]* "\n"
+
+ // a block is a properly-indented (that is, non-outdent-matching)
+ // sequence of lines. It DOES NOT include the trailing newline.
+ block = block:: " "* "\n" indent! body
+ &~ " "* "\n" outdent ~[.] ~[]*
+
+ // a body is what's left of a block after you remove the indentation
+ // from the first line
+ body = leaf
+ | interior
+
+ leaf = "leaf":: [a-z0-9]++
+ interior = "interior":: ("":: [a-z0-9]++) " "* block block
+
+}