Tokens:
	
	{ } ( ) [ ] ; :
        +-
        OPPUNC,
	COMPOP,
        STMTKEY, USING, TRUE, FALSE,
        CHECK, EXPECT, THROW, ABOUT, ARRAY, TRACE,
        FIXTURE, TEST, TESTS, SUITE, SETUP, TEARDOWN,
	IDENT, STRLIT, CHARLIT, NUMLIT,
	HASHLINE,

Grammar:

top:
	prologue-opt TEST SUITE IDENT { the-fixture-opt the-tests-opt } ;-opt
		FIRST = { HASHLINE, USING, TEST }
		FOLLOW = { $ }

prologue-opt:
	e
	prologue
		FIRST = { e, HASHLINE, USING }
		FOLLOW = { TEST }

prologue:
	prologue-stmt prologue-opt
		FIRST = { HASHLINE, USING }
		FOLLOW = { TEST }

prologue-stmt:	
	HASHLINE
	USING junk ;
		FIRST = { HASHLINE, USING }
		FOLLOW = { HASHLINE, USING, TEST }

the-fixture-opt:
	e
	the-fixture
		FIRST = { e, FIXTURE }
		FOLLOW = { TESTS, RCB }

the-fixture:
	FIXTURE : decls-opt the-setup-opt the-teardown-opt
		FIRST = { FIXTURE }
		FOLLOW = { TESTS, RCB }

decls-opt:
	e
	decls
		FIRST = { e, TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }
		FOLLOW = { SETUP, TEARDOWN, TESTS, RCB }

decls:
	simple-stmt decls-opt
		FIRST = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }
		FOLLOW = { SETUP, TEARDOWN, TESTS, RCB }
  /* Only statements of the form "typedecl ident = value;" will be
   * accepted but invalid ones will be easier to eliminate in a later
   * phase of the parser/compiler. */

the-setup-opt:
	e
	SETUP block
		FIRST = { e, SETUP }
		FOLLOW = { TEARDOWN, TESTS, RCB }

the-teardown-opt:
	e
	TEARDOWN block
		FIRST = { e, TEARDOWN }
		FOLLOW = { TESTS, RCB }

the-tests-opt:
	e
	the-tests
		FIRST = { e, TESTS }
		FOLLOW = { RCB }

the-tests:
	TESTS : all-tests-opt
		FIRST = { TESTS }
		FOLLOW = { RCB }

all-tests-opt:
	e
	all-tests
		FIRST = { e, TEST }
		FOLLOW = { RCB }

all-tests:
	one-test all-tests-opt
		FIRST = { TEST }
		FOLLOW = { RCB }

one-test:
	TEST IDENT block
		FIRST = { TEST }
		FOLLOW = { TEST, RCB }

block:
	{ stmt-seq-opt }
		FIRST = { LCB }
		FOLLOW = { TEARDOWN, TESTS, RCB,
		TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TEST, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, (, CHECK, ;, LCB, STMTKEY,
		RCB }

stmt-seq-opt:
	e
	stmt-seq
		FIRST = { e, TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, (, CHECK, ;, LCB, STMTKEY }
		FOLLOW = { RCB }

stmt-seq:
	stmt stmt-seq-opt
		FIRST = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, (, CHECK, ;, LCB, STMTKEY }
		FOLLOW = { RCB }

stmt:
	CHECK ( junk ) EXPECT expectation ;
	;
	simple-stmt
	block
	STMTKEY stmt-key-tail
		FIRST = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, (, CHECK, ;, LCB, STMTKEY }
		FOLLOW = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TEST, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, (, CHECK, ;, LCB, STMTKEY,
		RCB }

expectation:
	TRUE
	FALSE
	THROW
	ABOUT junk +- junk
	ARRAY junk
	TRACE
	COMPOP junk
		FIRST = { TRUE, FALSE, THROW, ABOUT, ARRAY, TRACE, COMPOP }
		FOLLOW = { ; }

simple-stmt:
	stmt-start-junk ;
		FIRST = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }
		FOLLOW = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TEST, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, (, CHECK, ;, LCB, STMTKEY,
		RCB }

stmt-key-tail:
	( junk-or-semi ) stmt
	stmt
		FIRST = { TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, (, CHECK, ;, LCB, STMTKEY }
		FOLLOW = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TEST, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, (, CHECK, ;, LCB, STMTKEY,
		RCB }
  /* note FIRST conflict: ( is in FIRST(stmt).  If ( begins
   * stmt-key-tail, parse as first option.  Grammar not technically
   * LL(1) but way simpler to write this way. */
  /* Handles if, else, switch, while, do, and both kinds of for, as 
   * well as common student errors like "else (x > 2) { blah; }" or
   * "for (int x = 2; ++x) { ... }"---these unsyntactic statements will
   * be passed thru to C++ compiler for better/consistent error
   * messaging. */

junk-or-semi:
	e
	; junk-or-semi
	junk junk-or-semi
		FIRST = { e, TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, (, ; }
		FOLLOW = { ) }

junk-opt:
	e
	junk
		FIRST = { e, TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }
		FOLLOW = { ), ], RCB }

junk:
	stmt-word junk-tail
	stmt-start-junk
		FIRST = { TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }
		FOLLOW = { ;, ), +-, 
		TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }

stmt-start-junk:
	STRLIT junk-tail
	NUMLIT junk-tail
	CHARLIT junk-tail
	IDENT junk-tail
	OPPUNC junk-tail
	( junk-opt ) junk-tail
	expr-word junk-tail
		FIRST = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, SUITE, 
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }
		FOLLOW = { ;, ), +-, 
		TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }
  /* Note that e.g. "5 +- 3" is legal cpp (i.e. "5 + (-3)") but not
   * valid here, because +- is its own token. */
  /* Note that this enforces balanced brackets on (), [], and {}, but
   * not for <>.  This is because < and > are also binary operators. */

junk-tail:
	e
	COMPOP junk-tail
	: junk-tail
	[ junk-opt ] junk-tail
	{ junk-opt } junk-tail
	junk
		FIRST = { e, TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, (, COMPOP, :, [, LCB }
		FOLLOW = { ;, ), +-, 
		TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }

stmt-word:
	CHECK
	SETUP
	TEARDOWN
	TEST
	TESTS
		FIRST = { CHECK, SETUP, TEARDOWN, TEST, TESTS }
		FOLLOW = { ;, ), +-,
		TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, ( }

expr-word:
	TRUE
	FALSE
	THROW
	EXPECT
	ABOUT
	ARRAY
	TRACE
	FIXTURE
	SUITE
		FIRST = { TRUE, FALSE, THROW, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, SUITE }
		FOLLOW = { TRUE, FALSE, THROW, CHECK, EXPECT, ABOUT,
		ARRAY, TRACE, FIXTURE, TESTS, TEST, SUITE, SETUP, TEARDOWN,
		STRLIT, NUMLIT, IDENT, OPPUNC, (, COMPOP, :, [, LCB,
		;, ), +- }
  /* These words are "regular" C++ idents or keywords when they appear
   * in expressions (and aren't being used as unci keywords). */
