/*
 * this is to carry all the state associated with I/O input format
 */
typedef struct {
  FILE *input;
  FILE *output;
  regex_t preg;
  int unget;
} io_state_t;


/*
 * returns an indent string. The buffer is static
 */
char *get_indent (int level);

/*
 * returns a heap allocated representation of the value
 */
char *format_value (token_t token);

/*
 * returns the next line from input, buffer is static
 */
int readline (FILE *input, char **buff_in, size_t *buff_size_in);

/*
 * the last token will be returned again
 */
void unget_token (io_state_t *io);

/*
 * returns the next token
 */
int get_token (io_state_t *io, token_t *token);

/*
 * allocates heap storage for the in.value string of this token, so it can
 * be kept
 */
token_t copy_token (token_t in);

/*
 * runs the parser with this input and output
 */
void parser (FILE *input, FILE *output);

/*
 * top level nanologo grammar parser
 *
 * prints the header and footer for Python stuff, runs until an EOF token is
 * reached.
 */
int parse_program (io_state_t *io);

/*
 * parses a [ ... ] block
 *
 * runs until a ']' token is reached
 */
int parse_subprogram (io_state_t *io, int indent, hash_table_t *variable);

/*
 * parses a keyword and arguments.
 *
 * This pair of functions handles *all* keywords, but it's still recursive
 * decent. It consults the keywords array (see keywords.h) to discover what is
 * a keyword. Actions are dictated by the keyword_handler array, which contains
 * pointers to handler functions. It will recurse down the keyword array until
 * it matches the token or reaches the NULL sentinel at the end of the keyword
 * array, which indicates that the current token is not a keyword.
 *
 * It's recursive decent because if it doesn't match the current keyword being
 * tested (given by the argument key), it will call itself with key + 1.
 *
 * parse_keyword is the top-level function to handle setup and tear-down stuff
 * before and after the parsing, parse_keyword_n does the rest.
 */
int parse_keyword (io_state_t *io, int indent, hash_table_t *variable);
int parse_keyword_n (io_state_t *io, int indent, hash_table_t *variable,
		     token_t token, int i);

/*
 * generic handler for keywords that take zero arguments. See keywords.h for
 * the keyword_format array.
 */
int parse_noargs (io_state_t *io, int indent, token_t token, int key,
		  hash_table_t *variable);

/*
 * generic handler for keywords that take one argument See keywords.h for the
 * keyword_format array.
 */
int parse_onearg (io_state_t *io, int indent, token_t token, int key,
		  hash_table_t *variable);

/*
 * handles the "make" keyword, which always takes two arguments
 */
int parse_make (io_state_t *io, int indent, token_t token, int key,
		hash_table_t *variable);

/*
 * handler for "incr" and "decr" keywords, which always take two arguments,
 * the first of which must be a defined identifier
 */
int parse_incdec (io_state_t *io, int indent, token_t token, int key,
		  hash_table_t *variable);

/*
 * handler for the "output" keyword, which can take a value or a string
 */
int parse_output (io_state_t *io, int indent, token_t token, int key,
		  hash_table_t *variable);

/*
 * handler for the "color" keyword, which must take a string
 */
int parse_color (io_state_t *io, int indent, token_t token, int key,
		 hash_table_t *variable);

/*
 * handler for the "write" keyword, which must take a string
 */
int parse_write (io_state_t *io, int indent, token_t token, int key,
		 hash_table_t *variable);

/*
 * handler for the "goto" keywords, which always take two arguments
 */
int parse_goto (io_state_t *io, int indent, token_t token, int key,
		hash_table_t *variable);

/*
 * handler for the "repeat" keyword, which always takes two arguments, the
 * second of which must be a '['
 */
int parse_repeat (io_state_t *io, int indent, token_t token, int key,
		  hash_table_t *variable);

/*
 * handler for the "if" keyword, which always takes two arguments, the second
 * of which must be a '['
 */
int parse_if (io_state_t *io, int indent, token_t token, int key,
	      hash_table_t *variable);

/*
 * parses a number, if that fails it recurses on parse_identifier
 *
 * arg->value is set to point to heap allocated storage for the string if
 * successful. The results to arg are undefined on failure, but it isn't set to
 * point to anything on the heap in that case.
 */
int parse_number (io_state_t *io, token_t *arg, hash_table_t *variable);

/*
 * parses an identifier WITHOUT a check that it is defined
 *
 * arg->value is set to point to heap allocated storage for the string if
 * successful. The results to arg are undefined on failure, but it isn't set to
 * point to anything on the heap in that case.
 */
int parse_identifier_nodefcheck (io_state_t *io, token_t *arg);

/*
 * parses an identifier WITH a check that it is defined
 *
 * arg->value is set to point to heap allocated storage for the string if
 * successful. The results to arg are undefined on failure, but it isn't set to
 * point to anything on the heap in that case.
 */
int parse_identifier (io_state_t *io, token_t *arg, hash_table_t *variable);

/*
 * parses an openbrace token. All this really needs to do is return TRUE
 * or FALSE, or exit with error if the brace is not expected.
 */
int parse_openbrace (io_state_t *io, int openbrace_expected);

/*
 * parses a closebrace token. All this really needs to do is return TRUE
 * or FALSE, or exit with error if the brace is not expected.
 */
int parse_closebrace (io_state_t *io, int closebrace_expected);

/*
 * parses an EOF token. All this really needs to do is return TRUE
 * or FALSE, or exit with error if the brace is not expected.
 *
 * I use this heavily as a kind of "assert(!EOF)" type thing. This is always
 * called on a token before anything else attempts to parse it, which is how
 * I catch EOF's that try to sneak up on me.
 */
int parse_eof (io_state_t *io, int eof_expected);

