/*
 *
 * Licensed under the GPL. <www.gnu.org>

rEXPRESSION[, rEXPRESSION...]:IDENTIFIER[/IDENTIFIER]*:EXPRESSION
	GROUP
	| [rEXPRESSION[, rEXPRESSION] VAR=rEXPRESSION [VAR=rEXPRESSION]*]*
::IDENTIFIER


GUARD --':'-![:=]--> GROUP-HEAD --> GROUP-BODY (--> ::GROUP-IDENTIFIER)
  |        \--':'--> GROUP-IDENTIFIER (closegroup)
  |        \--'='--> GROUP-HEAD (callgroup)
  \----!':'--> SETTING


EXPRESSION == /([^\s=]+)|"([^"]*)"/
GUARD= (rEXPRESSION(, rEXPRESSION)*)?

SETTINGs must have an unquoted = in them.
GUARDS never have an unquoted = in them.

 First-item blank-GUARD GROUPs are only default if named DEFAULT. Otherwise,
 	they're GROUPERS.
 First-item blank-GUARD SETTINGs remain defaults.

 To share DEFAULTs, define a GROUP and call it with :=
  :OTHERGROUP/define: ::OTHERGROUP
  :DEFAULT: :=OTHERGROUP: ::DEFAULT


 A GROUPer following a GUARD with no SETTINGS (to trigger the default)
  will look like a GUARDed GROUP. ?Workaround: Guard :=DEFAULT:  or group
  the guards with a grouper.

 A SETTING with no GUARDS, following a GROUP, will raise an error (ie it won't
  set a default).
*/

#include <stdio.h>
#include <stdlib.h>

#include "skiplist.h"

/*----------------------------------------------------------------------*/
/* File parsing 
 *
 *  File is read line-by-line into a string. If the string is too short,
 *   it's realloc'd.
 *  Parsing is through eat_a(), read_id(), and read_expression()
 *   Note that although eat_a() skips whitespace, if asked to eat a '\n',
 *   it returns true if the whitespace turns out to contain one.
 */

typedef struct { FILE *handle, char *name, int marks } BOOK;
typedef struct { BOOK *book, fpos_t mark } BOOKMARK;
typedef struct { BOOKMARK *bookmark, int ix } CONTEXT;



/*----------------------------------------------------------------------*\
    I'm going to be parsing and executing this file at the same time.
\*----------------------------------------------------------------------*/

int group_body(
	char *group_name,
	char *key,
	enum guard_was during_match,
	CONTEXT *context
) {

	char first_item=TRUE;

	while ( !c_eof(context) ) { /* eof() is effectively "::\:file" */
		
		enum guard_was 
			guard_was=read_guard(key, during_match);
			
		/* Returns GUARD_MATCHED GUARD_FAILED GUARD_WAS_BLANK */

		if ( guard_was==GUARD_MATCHED )   { trigger_defaults(); }

		if ( eat_a(':') ) {
			if ( eat_a(':') ) {
				char *id=read_id();
				if ( strcmp(group_name,id) ) {
					die("Misnested groupend: expected %s",group_name);
				}
				free(id);
				return; /* Found end-of-group */
			}
			else {
				int call=eat_a('=');
				char *group_qname=read_qid();
				char *expression=NULL;
				if ( ! eat_a(':') ) {
					die("Expected a colon here.");
				}
				if ( eat_a('\n') ) {
					expression=strdup(default_expression);
				}
				else {
					expression=read_expression();
				}
				{
					char *eval=expand_expression();

			# Going to need to keep a context for DEFAULTs.
					
					if ( call ) {
						call_group(group_name, eval, guard_matched);
					}
					else {
						group_body(group_name, eval, guard_matched);
					}
					free(eval);
				}
				free(expression);
				free(group_qname);
			}
		}
		else {
			if ( first_item && guard_was==GUARD_WAS_BLANK ) {
				record_default_setting();
			}
			else {
				switch ( read_setting(guard_was) ) {
				case SETTING_WAS_BLANK:
					switch ( guard_was ) {
					case GUARD_WAS_BLANK:
						die("Blank guard followed by blank setting: Syntax?")
					case GUARD_MATCHED: /* Default triggered earlier */
					case GUARD_FAILED:
						break;
					default:
						die("Unexpected return from read_guard()");
					}
				case SETTING_SET: /* Side-effect has no bearing on parsing */
				case SETTING_NOT_SET:
					switch ( guard_was ) {
					case GUARD_WAS_BLANK:
						die("Blank guard appeared too late to be a DEFAULT")
					case GUARD_MATCHED: /* Default triggered earlier */
					case GUARD_FAILED:
						break;
					default:
						die("Unexpected return from read_guard()");
					}
				default:
					die("Unexpected return from read_setting()");
				}
			}
		}
	} /* endwhile( !eof() ) */

/* This is not the normal exit for a GROUP.
   This is the normal exit for an include. */

	if ( *group_name!=':' ) {
		die("Unexpected end-of-file '%s' in group %s",
			(*context)->bookmark[(*context)->ix]->book->name,
			group_name);
	}
	else {
		if ( strcmp(group_name+1,
					(*context)->bookmark[(*context)->ix]->book->name) ) {
			die("Inclusion mismatch: while in %s, %s closed.",
				group_name,
				(*context)->bookmark[(*context)->ix]->book->name);
		}
	}
	
	c_close(context);
	
	return;
}

int main ( int argc, char **argv ) {
	c_open(&context,"vars.conf");
	group_body(context->&context,
	char *group_name,
	char *key,
	enum guard_was during_match,
	CONTEXT *context
}

