/* Contains code from Marc Feeley's tinyc.c, with permission. */
/* http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c */
/* Heavily modified by Chris Curl to add more C-like support */
/*
* This is a compiler for Tiny-C, a stripped down version of C.
* The compiler reads the program from the file named on the command line
* (default unit-tests.tc), then runs it in the built-in VM. Output comes only from emit().
* The grammar of Tiny-C in EBNF is:
*
* <program> ::= { <def> } (must define main)
* <include> ::= "include" '"' <file_name> '"' ";" (splices the file in place; each file is read once)
* main is a <func_def> with no parameters and return type "int" or "void"; it must be defined exactly once.
* <defs> ::= { <def> }
* <def> ::= <func_def> | <var_def>
* <func_def> ::= ("void" | "int" | "byte") <id> "(" [ <parameter> { "," <parameter> } ] ")" "{" { <statement> } "}"
* <parameter> ::= ("int" | "byte") <id> [ "[" "]" ]
* <local_decl> ::= ("int" | "byte") <id> [ "=" <expr> ] { "," <id> [ "=" <expr> ] } ";"
* <var_def> ::= ("int" | "byte") <var_decl> { "," <var_decl> } ";"
* <var_decl> ::= <id> [ "[" <int> "]" ] | <id> "=" <constant_expr> | <id> "[" [ <int> ] "]" "=" <string>
* <statement> ::= <local_decl> |
* <void_func_call> ";" |
* "return" [ <expr> ] ";" |
* "if" <paren_expr> <statement> |
* "if" <paren_expr> <statement> "else" <statement> |
* "while" <paren_expr> <statement> |
* "do" <statement> "while" <paren_expr> ";" |
* "{" { <statement> } "}" |
* <expr> ";" |
* ";"
* <paren_expr> ::= "(" <expr> ")"
* <expr> ::= <test> | <id> "=" <expr> | <id> "[" <expr> "]" "=" <expr>
* <incdec> ::= ("++" | "--") <scalar_id> | <scalar_id> ("++" | "--") (scalar locals, parameters, and globals only; postfix yields the old value, prefix the new)
* <test> ::= <math> | <math> "<" <math> | <math> ">" <math> | <math> "==" <math> | <math> "!=" <math>
* <math> ::= <term> | <math> <math_op> <term> (no precedence: applied left to right)
* <math_op> ::= "+" | "-" | "*" | "/" | "%"
* <term> ::= <id> | <func_call> | <id> "[" <expr> "]" | <array_ref> | <incdec> | <int> | <char> | <string> | <paren_expr> | "-" <term>
* <func_call> ::= (<int_func_id> | <byte_func_id>) "(" [ <expr> { "," <expr> } ] ")"
* <void_func_call> ::= <void_func_id> "(" [ <expr> { "," <expr> } ] ")"
* <array_ref> ::= <global_array_id> | <array_parameter_id>
* <array_arg> ::= <array_ref> | <string> (a string literal is valid only as an array argument or a global array initializer)
* <int_func_id> and <byte_func_id> are IDs of functions returning a value;
* void calls are statements, not terms.
* emit(int value) is a built-in VM service that writes the low byte and
* returns the written byte.
* <id> ::= [A-Z|a-z|_][A-Z|a-z|0-9|_]* (at most 63 characters)
* Reserved words: do, else, if, while, void, int, byte, emit, return, include. "emit" is the built-in function, so it cannot name a variable, parameter, local, or function.
* A local or parameter may shadow a global or function name.
* int and byte functions must return on every path. A while loop does not count as returning, so "while (1) { return 1; }" is rejected.
* <int> ::= <a_decimal_integer> (no sign; use unary minus. Overflow is not detected)
* <char> ::= "'" <single_character_or_escape> "'"
* <string> ::= '"' { <string_character_or_escape> } '"'
* Comments start with "//" and run to the end of the line; /* */ comments are not supported.
* Character literals are int-valued and represent the numeric value of one byte; supported escapes
* are '\n', '\r', '\t', '\0', '\e', '\\', '\'', and '\"'.
* Byte objects use unsigned 8-bit values (0 to 255) stored as bytes for globals and arrays, and in the VM's int slots for locals and parameters. Assignments,
* byte parameters, returns, and byte array stores narrow to this range; byte values
* promote to int in expressions. Conversions are implicit; casts are not supported.
* Scalar initializers are supported. Global initializers must be constant expressions
* and are folded at parse time; local initializers are assignments run at the declaration.
* String initializers are global byte arrays laid out as [length low byte, length high byte, ASCII values..., 0].
* The length excludes the final zero; an omitted array size is inferred, and an
* explicit size must fit the length and terminator. Non-ASCII characters are
* rejected outside comments and character literals. String escapes are '\n', '\r', '\t', '\0', '\e', '\\', '\'', and '\"'.
* String literals used as array arguments get anonymous global storage. They must be
* passed to byte[] parameters; other array arguments must match their element type.
* String literals are valid only as array arguments or global array initializers.
* Array parameters pass global-array base slots by reference.
* Array indexes are unchecked at runtime.
* The VM service instruction is encoded as SYS followed by a one-byte service ID.
*
* The compiler does a minimal amount of error checking to help
* highlight the structure of the compiler.
*/