/*
 * Copyright (c) 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Flick is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>

#include <mom/c/libcast.h>
#include <mom/c/scml.hh>

#define min(x,y) ((x < y) ? x : y)
#define max(x,y) ((x > y) ? x : y)

struct scml_handler_table *scml_context::sht = 0;

scml_context::scml_context()
{
	this->flags = 0;
	this->stream_pos = 0;
	this->scope = 0;
	this->lvalues = create_tag_list(0);
	this->rvalues = this->lvalues;
	this->offset_size = 0;
	this->indent_size = 0;
	this->ws_queue = 0;
}

scml_context::~scml_context()
{
}

void scml_context::set_flags(int the_flags)
{
	this->flags = the_flags;
}

int scml_context::get_flags()
{
	return( this->flags );
}

void scml_context::set_parent(struct scml_context *sc)
{
	this->parent = sc;
}

struct scml_context *scml_context::get_parent()
{
	return( this->parent );
}

void scml_context::set_stream_pos(struct scml_stream_pos *ssp)
{
	this->stream_pos = ssp;
}

struct scml_stream_pos *scml_context::get_stream_pos()
{
	return( this->stream_pos );
}

void scml_context::set_cmd_def(struct scml_cmd_definition *the_def)
{
	this->def = the_def;
}

struct scml_cmd_definition *scml_context::get_cmd_def()
{
	return( this->def );
}

void scml_context::set_lvalues(tag_list *tl)
{
	this->lvalues = tl;
}

tag_list *scml_context::get_lvalues()
{
	return( this->lvalues );
}

void scml_context::set_rvalues(tag_list *tl)
{
	this->rvalues = tl;
}

tag_list *scml_context::get_rvalues()
{
	return( this->rvalues );
}

void scml_context::set_scope(struct scml_scope *the_scope)
{
	this->scope = the_scope;
}

struct scml_scope *scml_context::get_scope()
{
	return( this->scope );
}

void scml_context::set_offset_size(int size)
{
	this->offset_size = size;
}

int scml_context::get_offset_size()
{
	return( this->offset_size );
}

void scml_context::set_indent_size(int size)
{
	this->indent_size = size;
}

int scml_context::get_indent_size()
{
	return( this->indent_size );
}

void scml_context::set_ws_queue(int size)
{
	this->ws_queue = size;
}

int scml_context::get_ws_queue()
{
	return( this->ws_queue );
}

void scml_context::locate_scope(struct scml_token *st,
				struct scml_scope **inout_scope,
				const char **out_id)
{
	switch( st->kind ) {
	case SCML_TERM_ID:
		*out_id = st->value.id;
		break;
	case SCML_NT_SCOPE_RES:
		if( (st->value.children[1].kind == SCML_TERM_ID) ) {
			struct scml_scope *child_scope;
			
			*out_id = 0;
			this->locate_scope(&st->value.children[0],
					   inout_scope,
					   out_id);
			if( *out_id && *inout_scope &&
			    (child_scope = (*inout_scope)->
			     find_child(*out_id)) ) {
				*out_id = st->value.children[1].value.id;
				*inout_scope = child_scope;
			}
		} else {
			scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
				   "Non-ID(%s) used in scope resolution",
				   scml_kind_map[st->value.children[1].kind]);
			*inout_scope = 0;
			*out_id = 0;
		}
		break;
	case SCML_NT_NAME:
		this->locate_scope(&st->value.children[0],
				   inout_scope,
				   out_id);
		break;
	default:
		scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
			   "Non-ID(%s) used in scope resolution",
			   scml_kind_map[st->value.children[1].kind]);
		*inout_scope = 0;
		*out_id = 0;
		break;
	}
}

void scml_context::print_indent(int size)
{
	int lpc;
	
	for( lpc = 0; lpc < size; lpc++ ) {
		w_printf(" ");
	}
}

int scml_context::print_token(struct scml_token *st)
{
	int retval = 1;
	
	switch( st->kind ) {
	case SCML_COL_POS:
	case SCML_ROW_POS:
		break;
	case SCML_TERM_BOOL:
		this->print_indent(this->ws_queue);
		this->ws_queue = 0;
		w_printf("%s", st->value.b ? "true" : "false");
		break;
	case SCML_TERM_INT:
		this->print_indent(this->ws_queue);
		this->ws_queue = 0;
		w_printf("%d", st->value.i);
		break;
	case SCML_TERM_FLOAT:
		this->print_indent(this->ws_queue);
		this->ws_queue = 0;
		w_printf("%f", st->value.f);
		break;
	case SCML_TERM_STRING:
		st->value.str->print(this->ws_queue);
		this->ws_queue = 0;
		break;
	case SCML_TERM_TAG:
		/*
		 * If its a tag ref we need to figure out what its
		 * referring to, in order to print it
		 */
		if( (st->value.ti->data.kind == TAG_REF) ) {
			struct scml_token_sequence *ref_sts;
			struct scml_string *ref_ss;
			
			if( (ref_sts = scml_token_sequence::
			     ptr(st->value.ti->data.
				 tag_data_u.ref)) ) {
				int old_offset, old_indent;
				
				/*
				 * Its scml code so we need to adjust ourself
				 * to the formatting of this sequence and
				 * print it out.
				 */
				old_offset = this->offset_size;
				this->offset_size = ref_sts->get_indent();
				old_indent = this->indent_size;
				if( this->flags & SCF_PREFORMATTED )
					this->indent_size = this->stream_pos->
						get_column() - old_offset;
				retval = this->format_sequence(ref_sts);
				this->indent_size = old_indent;
				this->offset_size = old_offset;
			} else if( (ref_ss = scml_string::
				    ptr(st->value.ti->data.tag_data_u.ref)) ) {
				/* An encapsulated string */
				ref_ss->print(this->ws_queue);
				this->ws_queue = 0;
			} else {
				scml_alert(this->stream_pos,
					   SAF_WARNING|SAF_INTERNAL,
					   "Can't handle ref tag (%s)",
					   st->value.ti->data.tag_data_u.ref);
			}
		} else {
			union tag_data_u data;
			
			/* Just a regular tag, print it */
			data = get_tag_data(&st->value.ti->data, 0);
			print_tag_data(this->ws_queue,
				       st->value.ti->data.kind,
				       data);
			this->ws_queue = 0;
		}
		break;
	case SCML_TERM_TEXT: {
		int row, col;
		const char *curr;
		
		/* Track the row/column when printing (for errors) */
		row = this->stream_pos->get_row();
		col = this->stream_pos->get_column();
		for( curr = st->value.text; *curr; curr++ ) {
			/*
			 * Check for tabs that cross over the indentation
			 * of the sequence.  We'll need to fill it out with
			 * spaces rather than printing the tab.
			 */
			if( (this->flags & SCF_PREFORMATTED) &&
			    (*curr == '\t') &&
			    (col < this->offset_size) &&
			    ((col + 8) > this->offset_size) ) {
				this->ws_queue = (col + 8) - this->offset_size;
			} else if( isspace(*curr) &&
				   (!(this->flags & SCF_PREFORMATTED) ||
				    (*curr != '\n')) ) {
				if( !(this->flags & SCF_IGNORE_WHITE_SPACE) &&
				    ((this->flags & SCF_PREFORMATTED) ||
				     (this->flags & SCF_PRINTING_BODY)) &&
				    (col >= this->offset_size) ) {
					if( this->flags & SCF_PREFORMATTED )
						this->ws_queue +=
							(*curr == '\t') ? 8 :
						1;
					else if( !this->ws_queue )
						this->ws_queue = 1;
				}
			} else if( !(this->flags & SCF_PREFORMATTED) ||
				   (col >= this->offset_size) ||
				   (*curr == '\n') ) {
				/* Print any queued white space */
				if( this->ws_queue ) {
					if( *curr != '\n' )
						this->print_indent(this->
								   ws_queue);
					this->ws_queue = 0;
				}
				/*
				 * Record that we are in the body of the
				 * sequence.
				 */
				this->flags |= SCF_PRINTING_BODY;
				w_printf("%c", *curr);
				/*
				 * If this is a newline and we have some
				 * extra indentation we'll need to print
				 * that first
				 */
				if( (this->flags & SCF_PREFORMATTED) &&
				    (*curr == '\n') &&
				    !(this->flags & SCF_IGNORE_INDENT) ) {
					this->print_indent(this->indent_size);
				}
			}
			switch( *curr ) {
			case '\n':
				row++;
				col = 0;
				break;
			case '\t':
				col += 8;
				break;
			default:
				col++;
				break;
			}
		}
		/* There was some left over white space */
		this->stream_pos->set_row(row);
		this->stream_pos->set_column(col);
		break;
	}
	case SCML_TERM_ESCAPE: {
		struct scml_escape *se = 0;
		struct scml_scope *curr;
		
		curr = this->scope;
		while( curr ) {
			if( (se = curr->get_escape_table()->
			     find_escape(st->value.escape)) ) {
				this->print_indent(this->ws_queue);
				this->ws_queue = 0;
				w_printf("%s", se->value);
				curr = 0;
			} else
				curr = curr->get_parent();
		}
		if( !se ) {
			scml_alert(this->stream_pos,
				   SAF_ERROR|SAF_RUNTIME,
				   "Couldn't find escape '%s'",
				   st->value.escape);
			retval = 0;
		}
		break;
	}
	case SCML_TERM_VERBATIM:
		this->print_indent(this->ws_queue);
		this->ws_queue = 0;
		w_printf("%s", st->value.text);
		break;
	case SCML_ERROR:
		retval = 0;
		scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
			   "There was an error in execution",
			   scml_kind_map[st->kind]);
		break;
	default:
		retval = 0;
		scml_alert(this->stream_pos, SAF_WARNING|SAF_INTERNAL,
			   "Token (%s) not handled",
			   scml_kind_map[st->kind]);
		break;
	}
	switch( st->kind ) {
	case SCML_COL_POS:
	case SCML_ROW_POS:
		break;
	default:
		/* We're printing out the body of the text */
		this->flags |= SCF_PRINTING_BODY;
		break;
	}
	return( retval );
}

int scml_context::equalize_tokens(struct scml_token *dest,
				  struct scml_token *src,
				  int count)
{
	int retval = SCML_ERROR;
	int min_kind = SCML_NT_MAX, max_kind = 0, common_kind;
	int lpc;
	
	/* Strip everything and figure out what the range of types is */
	for( lpc = 0; lpc < count; lpc++ ) {
		dest[lpc] = this->eval_token(&src[lpc]);
		dest[lpc].strip();
		min_kind = min(min_kind, dest[lpc].kind);
		max_kind = max(max_kind, dest[lpc].kind);
	}
	if( (min_kind != max_kind) ) {
		/*
		 * We don't have all the same kind of type so we promote
		 * them all to the most accomodating type.
		 */
		common_kind = max_kind;
		min_kind = SCML_NT_MAX;
		max_kind = 0;
		for( lpc = 0; lpc < count; lpc++ ) {
			dest[lpc].promote(common_kind);
			min_kind = min(min_kind, dest[lpc].kind);
			max_kind = max(max_kind, dest[lpc].kind);
		}
		if( min_kind == max_kind )
			retval = dest[0].kind;
	} else
		retval = dest[0].kind;
	return( retval );
}

struct scml_token scml_context::eval_token(struct scml_token *st)
{
	struct scml_token args[3];
	struct scml_token retval;
	
	retval.kind = SCML_ERROR;
	switch( st->kind ) {
	case SCML_NONE:
	case SCML_IGNORE:
	case SCML_DONE:
		retval.kind = SCML_NONE;
		break;
	case SCML_ERROR:
		break;
	case SCML_COL_POS:
		this->stream_pos->set_column(st->value.i);
		retval = *st;
		break;
	case SCML_ROW_POS:
		this->stream_pos->set_row(st->value.i);
		retval = *st;
		break;
	case SCML_TERM_BOOL:
	case SCML_TERM_INT:
	case SCML_TERM_FLOAT:
	case SCML_TERM_STRING:
	case SCML_TERM_TAG:
	case SCML_TERM_VERBATIM:
	case SCML_TERM_TEXT:
	case SCML_TERM_ESCAPE:
	case SCML_TERM_TAG_LIST:
	case SCML_TERM_SLASH:
	case SCML_NT_COMMAND:
		/* These dont require any evaluation */
		retval = *st;
		break;
	case SCML_NT_SET: {
		union tag_data_u data;
		tag_item *ti = 0;
		int index;
		
		/* Get the lvalue of a token, the tag and its array index */
		this->token_lvalue(&st->value.children[0], &ti, &index);
		if( ti ) {
			/* Evaluate the right side */
			args[0] = this->eval_token(&st->value.children[1]);
			args[0].strip();
			retval.value.ti = ti;
			/*
			 * Resolve any differences in types between the
			 * right and left sides.
			 */
			switch( args[0].kind ) {
			case SCML_TERM_BOOL:
				if( (ti->data.kind == TAG_ANY) ||
				    (ti->data.kind == TAG_ANY_ARRAY) ) {
					ti->data.kind = (tag_data_kind)
						((ti->data.kind & ~TAG_ANY) |
						 TAG_BOOL);
				}
				if( (ti->data.kind == TAG_BOOL) ||
				    (ti->data.kind == TAG_BOOL_ARRAY) ) {
					retval.kind = SCML_TERM_TAG;
					data.b = args[0].value.b;
					set_tag_data(&ti->data, index, data);
				}
				break;
			case SCML_TERM_INT:
				if( (ti->data.kind == TAG_ANY) ||
				    (ti->data.kind == TAG_ANY_ARRAY) ) {
					ti->data.kind = (tag_data_kind)
						((ti->data.kind & ~TAG_ANY) |
						 TAG_INTEGER);
				}
				if( (ti->data.kind == TAG_INTEGER) ||
				    (ti->data.kind == TAG_INTEGER_ARRAY) ) {
					retval.kind = SCML_TERM_TAG;
					data.i = args[0].value.i;
					set_tag_data(&ti->data, index, data);
				}
				break;
			case SCML_TERM_FLOAT:
				if( (ti->data.kind == TAG_ANY) ||
				    (ti->data.kind == TAG_ANY_ARRAY) ) {
					ti->data.kind = (tag_data_kind)
						((ti->data.kind & ~TAG_ANY) |
						 TAG_FLOAT);
				}
				if( (ti->data.kind == TAG_FLOAT) ||
				    (ti->data.kind == TAG_FLOAT_ARRAY) ) {
					retval.kind = SCML_TERM_TAG;
					data.f = args[0].value.f;
					set_tag_data(&ti->data, index, data);
				}
				break;
			case SCML_TERM_STRING:
				if( (ti->data.kind == TAG_ANY) ||
				    (ti->data.kind == TAG_ANY_ARRAY) ) {
					ti->data.kind = (tag_data_kind)
						((ti->data.kind & ~TAG_ANY) |
						 TAG_REF);
				}
				if( (ti->data.kind == TAG_STRING) ||
				    (ti->data.kind == TAG_STRING_ARRAY) ) {
					if( (data.str = args[0].value.str->
					     make_chars()) ) {
						retval.kind = SCML_TERM_TAG;
						set_tag_data(&ti->data, index,
							     data);
					}
				}
				if( (ti->data.kind == TAG_REF) ||
				    (ti->data.kind == TAG_REF_ARRAY) ) {
					retval.kind = SCML_TERM_TAG;
					data.ref = args[0].value.str->
						tag_ref();
					set_tag_data(&ti->data, index, data);
				}
				break;
			case SCML_TERM_TAG:
				if( (ti->data.kind == TAG_ANY) ||
				    (ti->data.kind ==
				     args[0].value.ti->data.kind) ) {
					retval.kind = SCML_TERM_TAG;
					ti->data = args[0].value.ti->data;
				}
				break;
			case SCML_TERM_TAG_LIST:
				if( (ti->data.kind == TAG_ANY) ||
				    (ti->data.kind == TAG_ANY_ARRAY) ) {
					ti->data.kind = (tag_data_kind)
						((ti->data.kind & ~TAG_ANY) |
						 TAG_TAG_LIST);
				}
				if( (ti->data.kind == TAG_TAG_LIST) ||
				    (ti->data.kind == TAG_TAG_LIST_ARRAY) ) {
					retval.kind = SCML_TERM_TAG;
					data.tl = args[0].value.tl;
					set_tag_data(&ti->data, index, data);
				}
				break;
			default:
				retval.kind = SCML_ERROR;
				break;
			}
			if( retval.kind == SCML_ERROR ) {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_TYPE,
					   "Incompatible types in assignment");
				args[0].print();
				print_tag(0, ti);
			}
		}
		break;
	}
	case SCML_NT_PLUS:
		retval.kind = this->equalize_tokens(args, st->value.children,
						    2);
		switch( retval.kind ) {
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i + args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.f = args[0].value.f + args[1].value.f;
			break;
		case SCML_TERM_STRING:
			retval.value.str = new scml_string;
			retval.value.str->concat(args[0].value.str);
			retval.value.str->concat(args[1].value.str);
			break;
		default:
			scml_alert(this->stream_pos, SAF_ERROR|SAF_TYPE,
				   "Types for '+' are inconsistent");
			args[0].print();
			args[1].print();
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	case SCML_NT_MINUS:
		retval.kind = this->equalize_tokens(args, st->value.children,
						    2);
		switch( retval.kind ) {
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i - args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.f = args[0].value.f - args[1].value.f;
			break;
		default:
			scml_alert(this->stream_pos, SAF_ERROR|SAF_TYPE,
				   "Types for '-' are inconsistent");
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	case SCML_NT_DIV:
		retval.kind = this->equalize_tokens(args, st->value.children,
						    2);
		switch( retval.kind ) {
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i / args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.f = args[0].value.f / args[1].value.f;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	case SCML_NT_MOD:
		retval.kind = this->equalize_tokens(args, st->value.children,
						    2);
		switch( retval.kind ) {
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i % args[1].value.i;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	case SCML_NT_MULT:
		retval.kind = this->equalize_tokens(args, st->value.children,
						    2);
		switch( retval.kind ) {
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i * args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.f = args[0].value.f * args[1].value.f;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	case SCML_NT_COND:
		args[0] = this->eval_token(st->value.children);
		args[0].strip();
		retval.kind = this->equalize_tokens(&args[1],
						    &st->value.children[1],
						    2);
		if( (retval.kind != SCML_ERROR) &&
		    ((args[0].kind == SCML_TERM_INT) ||
		     (args[0].kind == SCML_TERM_BOOL)) ) {
			if( args[0].value.i )
				retval = args[1];
			else
				retval = args[2];
		}
		break;
	case SCML_NT_EQUAL: {
		int cmp_kind;
		
		cmp_kind = this->equalize_tokens(args,
						 st->value.children,
						 2);
		retval.kind = SCML_TERM_BOOL;
		switch( cmp_kind ) {
		case SCML_TERM_STRING:
			retval.value.i = !args[0].value.str->
				cmp(args[1].value.str);
			break;
		case SCML_TERM_BOOL:
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i == args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.i = args[0].value.f == args[1].value.f;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	}
	case SCML_NT_NOT_EQUAL: {
		int cmp_kind;
		
		cmp_kind = this->equalize_tokens(args,
						 st->value.children,
						 2);
		retval.kind = SCML_TERM_BOOL;
		switch( cmp_kind ) {
		case SCML_TERM_STRING:
			retval.value.i = args[0].value.str->
				cmp(args[1].value.str);
			break;
		case SCML_TERM_BOOL:
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i != args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.i = args[0].value.f != args[1].value.f;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	}
	case SCML_NT_LT: {
		int cmp_kind;
		
		cmp_kind = this->equalize_tokens(args,
						 st->value.children,
						 2);
		retval.kind = SCML_TERM_BOOL;
		switch( cmp_kind ) {
		case SCML_TERM_STRING:
			retval.value.i = (args[0].value.str->
					  cmp(args[1].value.str) < 0);
			break;
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i < args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.i = args[0].value.f < args[1].value.f;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	}
	case SCML_NT_GT: {
		int cmp_kind;
		
		cmp_kind = this->equalize_tokens(args,
						 st->value.children,
						 2);
		retval.kind = SCML_TERM_BOOL;
		switch( cmp_kind ) {
		case SCML_TERM_STRING:
			retval.value.i = (args[0].value.str->
					  cmp(args[1].value.str) > 0);
			break;
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i > args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.i = args[0].value.f > args[1].value.f;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	}
	case SCML_NT_LE: {
		int cmp_kind;
		
		cmp_kind = this->equalize_tokens(args,
						 st->value.children,
						 2);
		retval.kind = SCML_TERM_BOOL;
		switch( cmp_kind ) {
		case SCML_TERM_STRING:
			retval.value.i = (args[0].value.str->
					  cmp(args[1].value.str) <= 0);
			break;
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i <= args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.i = args[0].value.f <= args[1].value.f;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	}
	case SCML_NT_GE: {
		int cmp_kind;
		
		cmp_kind = this->equalize_tokens(args,
						 st->value.children,
						 2);
		retval.kind = SCML_TERM_BOOL;
		switch( cmp_kind ) {
		case SCML_TERM_STRING:
			retval.value.i = (args[0].value.str->
					  cmp(args[1].value.str) >= 0);
			break;
		case SCML_TERM_INT:
			retval.value.i = args[0].value.i >= args[1].value.i;
			break;
		case SCML_TERM_FLOAT:
			retval.value.i = args[0].value.f >= args[1].value.f;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	}
	case SCML_NT_AND:
		break;
	case SCML_NT_LAND:
		retval.kind = this->equalize_tokens(args,
						    st->value.children,
						    2);
		switch( retval.kind ) {
		case SCML_TERM_INT:
		case SCML_TERM_BOOL:
			retval.kind = SCML_TERM_BOOL;
			retval.value.i = args[0].value.i && args[1].value.i;
			break;
		default:
			scml_alert(this->stream_pos, SAF_ERROR|SAF_TYPE,
				   "Types for '&&' are inconsistent");
			args[0].print();
			args[1].print();
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	case SCML_NT_OR:
		retval.kind = this->equalize_tokens(args,
						    st->value.children,
						    2);
		if( retval.kind == SCML_TERM_TAG_LIST ) {
			retval.value.tl = copy_tag_list(args[0].value.tl);
			concat_tag_list(retval.value.tl, args[1].value.tl);
		} else {
			scml_alert(this->stream_pos, SAF_ERROR|SAF_GENERAL,
				   "Couldn't get tag lists for union");
		}
		break;
	case SCML_NT_LOR:
		retval.kind = this->equalize_tokens(args,
						    st->value.children,
						    2);
		switch( retval.kind ) {
		case SCML_TERM_INT:
		case SCML_TERM_BOOL:
			retval.kind = SCML_TERM_BOOL;
			retval.value.i = args[0].value.i || args[1].value.i;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
		break;
	case SCML_NT_NOT:
		retval.kind = this->equalize_tokens(args,
						    st->value.children,
						    1);
		switch( retval.kind ) {
		case SCML_TERM_INT:
		case SCML_TERM_BOOL:
			retval.kind = SCML_TERM_BOOL;
			retval.value.i = !args[0].value.i;
			break;
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	case SCML_NT_EXPR:
		retval = this->eval_token(&st->value.children[0]);
		break;
	case SCML_NT_DOT: {
		args[0] = this->eval_token(&st->value.children[0]);
		args[0].strip();
		switch( args[0].kind ) {
		case SCML_TERM_TAG_LIST: {
			tag_item *ti;
			
			if( (st->value.children[1].kind == SCML_TERM_ID) &&
			    (ti = find_tag(args[0].value.tl,
					   st->value.children[1].value.id)) ) {
				retval.kind = SCML_TERM_TAG;
				retval.value.ti = ti;
			} else {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_GENERAL,
					   "Couldn't find tag");
				args[0].print();
				st->value.children[1].print();
			}
			break;
		}
		default:
			retval.kind = SCML_ERROR;
			break;
		}
		break;
	}
	case SCML_NT_SEL: {
		/* Evaluate the tag and its array index */
		args[0] = this->eval_token(&st->value.children[0]);
		args[1] = this->eval_token(&st->value.children[1]);
		args[1].strip();
		if( (args[0].kind == SCML_TERM_TAG) &&
		    (args[1].kind == SCML_TERM_INT) &&
		    (args[1].value.i <
		     (int) tag_data_length(&args[0].value.ti->data)) ) {
			tag_item *ti;
			tag_data td;
			
			/*
			 * We create a temporary tag to hold the value.  We
			 * have to do this since scml tokens don't encode
			 * every tag type so we just make a fake one so
			 * we can handle everything.  Of course it gets
			 * leaked which kinda sucks.
			 */
			td = create_tag_data((tag_data_kind)
					     (args[0].value.ti->data.kind &
					      ~TAG_ARRAY), 1);
			set_tag_data(&td, 0,
				     get_tag_data(&args[0].value.ti->data,
						  args[1].value.i));
			ti = create_tag_item("", &td);
			retval.kind = SCML_TERM_TAG;
			retval.value.ti = ti;
		} else {
			if( args[0].kind != SCML_TERM_TAG ) {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_RUNTIME,
					   "Couldn't get a tag for selection");
			} else if( args[1].kind != SCML_TERM_INT ) {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_RUNTIME,
					   "Selector isn't an integer");
			} else {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_RUNTIME,
					   "Selector %d is out of range",
					   args[1].value.i);
			}
		}
		break;
	}
	case SCML_NT_INIT: {
		union tag_data_u data;
		int error = 0, lpc;
		tag_data td;
		
		td = create_tag_data(TAG_ANY, 0);
		/*
		 * Walk through all the children of this token.  Each child
		 * evaluates to a tag so we just copy it to the tag list
		 */
		for( lpc = 0;
		     !error && (st->value.children[lpc].kind != SCML_NONE);
		     lpc++ ) {
			args[0] = this->eval_token(&st->value.children[lpc]);
			switch( args[0].kind ) {
			case SCML_TERM_BOOL:
				if( td.kind == TAG_ANY ) {
					td.kind = TAG_BOOL_ARRAY;
					retval.kind = SCML_TERM_TAG;
				}
				if( td.kind == TAG_BOOL_ARRAY ) {
					data.b = args[0].value.b;
					append_tag_data(&td, data);
				} else
					error = 1;
				break;
			case SCML_TERM_INT:
				if( td.kind == TAG_ANY ) {
					td.kind = TAG_INTEGER_ARRAY;
					retval.kind = SCML_TERM_TAG;
				}
				if( td.kind == TAG_INTEGER_ARRAY ) {
					data.i = args[0].value.i;
					append_tag_data(&td, data);
				} else
					error = 1;
				break;
			case SCML_TERM_FLOAT:
				if( td.kind == TAG_ANY ) {
					td.kind = TAG_FLOAT_ARRAY;
					retval.kind = SCML_TERM_TAG;
				}
				if( td.kind == TAG_FLOAT_ARRAY ) {
					data.f = args[0].value.f;
					append_tag_data(&td, data);
				} else
					error = 1;
				break;
			case SCML_TERM_STRING:
				if( td.kind == TAG_ANY ) {
					td.kind = TAG_REF_ARRAY;
					retval.kind = SCML_TERM_TAG;
				}
				if( td.kind == TAG_REF_ARRAY ) {
					data.ref = args[0].value.str->
						tag_ref();
					append_tag_data(&td, data);
				} else
					error = 1;
				break;
			case SCML_TERM_TAG:
				if( td.kind == TAG_ANY ) {
					td.kind = TAG_TAG_LIST;
					retval.kind = SCML_TERM_TAG_LIST;
					retval.value.tl = create_tag_list(0);
				}
				if( td.kind == TAG_TAG_LIST ) {
					*(add_tag(retval.value.tl,
						  args[0].value.ti->tag,
						  TAG_NONE)) =
						*(args[0].value.ti);
				} else
					error = 1;
				break;
			case SCML_TERM_TAG_LIST:
				if( td.kind == TAG_ANY ) {
					td.kind = TAG_TAG_LIST_ARRAY;
					retval.kind = SCML_TERM_TAG;
				}
				if( td.kind == TAG_TAG_LIST_ARRAY ) {
					data.tl = args[0].value.tl;
					append_tag_data(&td, data);
				} else
					error = 1;
				break;
			default:
				error = 1;
				break;
			}
		}
		if( lpc == 0 ) {
			retval.kind = SCML_TERM_TAG_LIST;
			retval.value.tl = create_tag_list(0);
		}
		if( error ) {
			retval.kind = SCML_ERROR;
			scml_alert(this->stream_pos,
				   SAF_ERROR|SAF_GENERAL,
				   "Cannot add incompatible type",
				   scml_kind_map[args[0].kind]);
		}
		if( retval.kind == SCML_TERM_TAG ) {
			retval.value.ti = create_tag_item("", &td);
		}
		break;
	}
	case SCML_NT_TAG: {
		tag_data_kind kind;
		union tag_data_u data;
		int size, lpc;
		tag_item *ti;
		tag_data td;
		
		/* This will construct a new tag with a specific type */
		this->tag_type(&st->value.children[1], &kind, &size);
		if( (st->value.children[0].kind == SCML_TERM_ID) &&
		    (kind != -1) ) {
			td = create_tag_data(kind, size);
			ti = create_tag_item(st->value.children[0].value.id,
					     &td);
			if( kind & TAG_ARRAY ) {
				/* Be nice and clear the memory */
				memset(&data, 0, sizeof( union tag_data_u ));
				for( lpc = 0; lpc < size; lpc++ ) {
					set_tag_data(&ti->data, lpc, data);
				}
			}
			retval.kind = SCML_TERM_TAG;
			retval.value.ti = ti;
		} else {
			if( kind == -1 ) {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_TYPE,
					   "Invalid type given for tag");
			}
		}
		break;
	}
	case SCML_TERM_ID:
		if( (retval.value.ti = find_tag(this->rvalues,
						st->value.id)) ||
		    (retval.value.ti = find_tag(this->scope->get_values(),
						st->value.id)) ) {
			retval.kind = SCML_TERM_TAG;
		} else {
			scml_alert(this->stream_pos, SAF_ERROR|SAF_GENERAL,
				   "Couldn't find tag \"%s\"",
				   st->value.id);
		}
		break;
	case SCML_NT_NAME: {
		struct scml_scope *var_scope;
		const char *id;
		
		var_scope = this->scope;
		while( var_scope->get_parent() )
			var_scope = var_scope->get_parent();
		this->locate_scope(st, &var_scope, &id);
		if( var_scope &&
		    (retval.value.ti = find_tag(var_scope->get_values(),
						id)) ) {
			retval.kind = SCML_TERM_TAG;
		} else {
			if( !var_scope ) {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_GENERAL,
					   "Couldn't locate scope");
			} else if( !retval.value.ti ) {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_GENERAL,
					   "Couldn't locate id (%s) in scope"
					   " %s", id, var_scope->get_name());
			}
		}
		break;
	}
	default:
		break;
	}
	return( retval );
}

void scml_context::token_lvalue(struct scml_token *st, tag_item **out_ti,
				int *out_index)
{
	switch( st->kind ) {
	case SCML_TERM_TAG:
		*out_ti = st->value.ti;
		*out_index = 0;
		break;
	case SCML_TERM_ID: {
		tag_item *ti;
		
		/* If its in the list of lvalues than just return that */
		if( (ti = find_tag(this->lvalues, st->value.id)) ) {
			*out_ti = ti;
		} else if( this->def &&
			   ((ti = find_tag(this->def->get_opt_params(),
					   st->value.id)) ||
			    (ti = find_tag(this->def->get_req_params(),
					   st->value.id))) ) {
			/*
			 * If its part of the optional or required parameters
			 * then we can safely add it to the lvalues.  This
			 * is used in parameter passing so that the parameter
			 * lists are preserved and we still get access to the
			 * variables.
			 */
			*out_ti = add_tag(this->lvalues, st->value.id,
					  TAG_NONE);
			(*out_ti)->data = ti->data;
		} else if( (ti = find_tag(this->scope->get_values(),
					  st->value.id)) ) {
			/* Its in the current scope */
			*out_ti = ti;
		} else {
			*out_ti = 0;
			scml_alert(this->stream_pos, SAF_ERROR|SAF_GENERAL,
				   "Couldn't find tag lvalue %s",
				   st->value.id);
		}
		*out_index = 0;
		break;
	}
	case SCML_NT_SEL: {
		struct scml_token t_index;
		
		/* Its an array index, return the same tag with an index */
		this->token_lvalue(&st->value.children[0], out_ti, out_index);
		t_index = this->eval_token(&st->value.children[1]);
		t_index.strip();
		if( t_index.kind == SCML_TERM_INT ) {
			*out_index = t_index.value.i;
		}
		else {
			*out_ti = 0;
			*out_index = 0;
		}
		break;
	}
	case SCML_NT_DOT:
		/* Its a tag within a tag list */
		if( st->value.children[1].kind == SCML_TERM_ID ) {
			this->token_lvalue(&st->value.children[0],
					   out_ti, out_index);
			if( *out_ti &&
			    (((*out_ti)->data.kind == TAG_TAG_LIST) ||
			     ((*out_ti)->data.kind == TAG_TAG_LIST_ARRAY)) ) {
				if( ((*out_ti) = find_tag(
					get_tag_data(&(*out_ti)->data,
						     *out_index).tl,
					st->value.children[1].
					value.id)) ) {
					*out_index = 0;
				}
			} else {
				*out_ti = 0;
			}
		}
		break;
	case SCML_NT_EXPR:
		this->token_lvalue(&st->value.children[0], out_ti, out_index);
		break;
	case SCML_NT_TAG: {
		struct scml_token t_tag;
		
		t_tag = this->eval_token(st);
		if( t_tag.kind == SCML_TERM_TAG ) {
			*out_ti = t_tag.value.ti;
			*out_index = 0;
		}
		else
			*out_ti = 0;
		break;
	}
	default:
		*out_ti = 0;
		break;
	}
}

int scml_context::handle_params(struct scml_token *cmd)
{
	int retval = 1;
	tag_item *ti;
	tag_list *tl;
	unsigned int lpc;
	
	/*
	 * Handle_params has to build the lvalues tag list from the parameters
	 * but it also has to have access to the parents values.  This
	 * inbetween state is accomplished by using the parents values as
	 * our rvalues and then token_lvalue takes care of building our lvalues
	 * from the parameter lists.
	 */
	this->rvalues = this->parent->lvalues;
	if( cmd->value.children[0].kind == SCML_TERM_SLASH )
		lpc = 2;
	else
		lpc = 1;
	for( ; cmd->value.children[lpc].kind != SCML_NONE; lpc++ ) {
		if( this->eval_token(&cmd->value.children[lpc]).kind ==
		    SCML_ERROR ) {
			retval = 0;
			scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
				   "Invalid arg");
			cmd->value.children[lpc].print();
		}
	}
	/* Add the optional paramters with their default values */
	tl = this->def->get_opt_params();
	for( lpc = 0; lpc < tl->items.items_len; lpc++ ) {
		if( !(ti = find_tag(this->lvalues,
				    tl->items.items_val[lpc].tag)) ) {
			add_tag(this->lvalues, tl->items.items_val[lpc].tag,
				TAG_NONE)->data =
				copy_tag_data(&tl->items.items_val[lpc].data);
		}
	}
	/* Make sure all required parameters were specified */
	tl = this->def->get_req_params();
	for( lpc = 0; lpc < tl->items.items_len; lpc++ ) {
		if( !(ti = find_tag(this->lvalues,
				    tl->items.items_val[lpc].tag)) ) {
			retval = 0;
			scml_alert(this->stream_pos, SAF_ERROR|SAF_GENERAL,
				   "Missing required parameter %s",
				   tl->items.items_val[lpc].tag);
		}
	}
	this->rvalues = this->lvalues;
	return( retval );
}

struct scml_cmd_definition *scml_context::find_cmd(
	struct scml_scope **cmd_scope,
	struct scml_token *st)
{
	struct scml_cmd_definition *retval = 0;
	
	switch( st->kind ) {
	case SCML_TERM_ID:
		retval = this->scope->find_cmd_definition(cmd_scope,
							  st->value.id);
		break;
	case SCML_NT_NAME: {
		struct scml_scope *new_cmd_scope;
		const char *id = 0;
		
		new_cmd_scope = this->scope;
		while( new_cmd_scope->get_parent() )
			new_cmd_scope = new_cmd_scope->get_parent();
		this->locate_scope(st, &new_cmd_scope, &id);
		if( new_cmd_scope )
			retval = new_cmd_scope->find_cmd_definition(cmd_scope,
								    id);
		break;
	}
	default:
		break;
	}
	return( retval );
}

struct scml_token_sequence *scml_context::get_contents(
	struct scml_cmd_definition *cmd_def,
	struct scml_token_sequence *sts,
	int start)
{
	struct scml_token_sequence *retval = 0;
	struct scml_cmd_definition *sub_cmd;
	struct scml_scope *cmd_scope;
	int depth = 1, lpc, len;
	struct scml_token *st;
	tag_list *tl, *tl_sl;
	tag_item *ti;
	char *ident;
	
	assert(cmd_def);
	/* These tag lists are used to track begin/end commands */
	tl = create_tag_list(0);
	tl_sl = create_tag_list(0);
	add_tag(tl, sts->get_value()[start - 1].value.children[0].
		get_identifier(), TAG_INTEGER, 1);
	/* Walk through the sequence trying to find the terminating command */
	for( lpc = start; (lpc < sts->get_length()) && depth; lpc++ ) {
		switch( sts->get_value()[lpc].kind ) {
		case SCML_NT_COMMAND: {
			int slash = 0;
			
			st = &sts->get_value()[lpc];
			switch( st->value.children[0].kind ) {
			case SCML_NT_EXPR:
				sub_cmd = 0;
				break;
			case SCML_TERM_SLASH:
				/* Record any terminating commands */
				slash = 1;
				sub_cmd = this->find_cmd(&cmd_scope,
							 &st->value.
							 children[1]);
				if( (ident = st->value.children[1].
				     get_identifier()) ) {
					if( (ti = find_tag(tl_sl, ident)) ) {
						ti->data.tag_data_u.i++;
						free(ident);
					} else {
						add_tag(tl_sl, ident,
							TAG_INTEGER, 1);
					}
				}
				break;
			case SCML_TERM_ID:
			case SCML_NT_NAME:
				/* Record any commands */
				sub_cmd = this->find_cmd(&cmd_scope,
							 &st->value.
							 children[0]);
				if( (ident = st->value.children[0].
				     get_identifier()) ) {
					if( (ti = find_tag(tl, ident)) ) {
						ti->data.tag_data_u.i++;
						free(ident);
					} else {
						add_tag(tl, ident,
							TAG_INTEGER, 1);
					}
				}
				break;
			default:
				sub_cmd = 0;
				break;
			}
			/*
			 * See if this command has been terminated,
			 * or if it was nested
			 */
			if( sub_cmd &&
			    (sub_cmd->get_flags() & SCDF_BRACKETED) &&
			    (cmd_def == sub_cmd) ) {
				if( slash )
					depth--;
				else
					depth++;
			}
			break;
		}
		default:
			break;
		}
	}
	if( depth ) {
		scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
			   "This is a malformed bracketed tag %d", depth);
	}
	/* Make sure all the terminators have corresponding initiators */
	len = lpc - start - 1;
	for( lpc = 0; lpc < (signed int)tl_sl->items.items_len; lpc++ ) {
		if( !find_tag(tl, tl_sl->items.items_val[lpc].tag) ) {
			scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
				   "Terminators without initiators for "
				   "command '%s'",
				   tl_sl->items.items_val[lpc].tag);
			depth = 1;
		}
	}
	/*
	 * Make sure every initiator is paired with a terminator.
	 * Note - Because some of the initiators and terminators could be
	 * defined within this sequence we can't be sure whether a command
	 * has a terminator.  We can only guess by checking to see if
	 * its terminator was ever used.  So we aren't able to check here
	 * whether a terminator was needed but never paired with its
	 * command.  However, this isn't a problem since the lack of a
	 * terminator will appear when you try and execute this sequence.
	 */
	for( lpc = 0; lpc < (signed int)tl->items.items_len; lpc++ ) {
		ti = find_tag(tl_sl, tl->items.items_val[lpc].tag);
		if( ti &&
		    (tl->items.items_val[lpc].data.tag_data_u.i >
		     ti->data.tag_data_u.i) ) {
			scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
				   "Not enough terminators for command '%s'",
				   ti->tag);
			depth = 1;
		} else if( ti &&
			   (tl->items.items_val[lpc].data.tag_data_u.i <
			    ti->data.tag_data_u.i) ) {
			scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
				   "Too many terminators for command '%s'",
				   ti->tag);
			depth = 1;
		}
		free(tl->items.items_val[lpc].tag);
	}
	delete_tag_list(tl);
	delete_tag_list(tl_sl);
	if( depth ) {
		scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
			   "This is a malformed bracketed tag %d", depth);
	} else {
		/* Build a new sequence corresponding to this subsequence */
		retval = new scml_token_sequence();
		retval->set_indent(this->get_stream_pos()->get_column());
		retval->set_stream_pos(sts->get_stream_pos());
		retval->set_value(&(sts->get_value()[start]));
		retval->set_length(len);
	}
	return( retval );
}

struct scml_token_sequence *scml_context::partition_sequence(
	struct scml_cmd_definition *cmd_def,
	struct scml_cmd_definition *partition,
	struct scml_token_sequence *sts)
{
	struct scml_token_sequence *retval = 0;
	int lpc, poss_pivot = -1, pivot = -1;
	struct scml_cmd_definition *sub_cmd;
	struct scml_scope *cmd_scope;
	struct ptr_stack *cmd_stack;
	struct scml_token *st;
	
	cmd_stack = create_ptr_stack();
	push_ptr(cmd_stack, cmd_def);
	/*
	 * get_contents has already done the error checking for us so we
	 * just need to find the subcommand and partition around it
	 */
	for( lpc = 0;
	     (lpc < sts->get_length()) && !empty_ptr_stack(cmd_stack) &&
		     (pivot == -1);
	     lpc++ ) {
		switch( sts->get_value()[lpc].kind ) {
		case SCML_NT_COMMAND: {
			int slash = 0;
			
			st = &sts->get_value()[lpc];
			switch( st->value.children[0].kind ) {
			case SCML_NT_EXPR:
				sub_cmd = 0;
				break;
			case SCML_TERM_SLASH:
				slash = 1;
				sub_cmd = this->find_cmd(&cmd_scope,
							 &st->value.
							 children[1]);
				if( sub_cmd != cmd_def )
					poss_pivot = -1;
				if( (top_ptr(cmd_stack) == sub_cmd) )
					pop_ptr(cmd_stack);
				else {
					while( top_ptr(cmd_stack) == 0 )
						pop_ptr(cmd_stack);
					if( top_ptr(cmd_stack) == sub_cmd )
						pop_ptr(cmd_stack);
				}
				break;
			default:
				sub_cmd = this->find_cmd(&cmd_scope,
							 &st->value.
							 children[0]);
				if( !sub_cmd ||
				    (sub_cmd->get_flags() & SCDF_BRACKETED) )
					push_ptr(cmd_stack, sub_cmd);
				break;
			}
			if( sub_cmd && (sub_cmd == partition) ) {
				poss_pivot = lpc;
				if( (ptr_stack_length(cmd_stack) == 1) ) {
					pivot = lpc;
				}
			}
			break;
		}
		default:
			break;
		}
	}
	delete_ptr_stack(cmd_stack);
	if( (poss_pivot != -1) && (pivot == -1) )
		pivot = poss_pivot;
	if( pivot != -1 ) {
		int old_len;
		
		/*
		 * Found the pivot command so adjust the current sequence to
		 * enclose the first part, and make a new sequence to enclose
		 * the second.
		 */
		old_len = sts->get_length();
		sts->set_length(pivot - 1);
		retval = new scml_token_sequence();
		retval->set_indent(sts->get_indent());
		retval->set_stream_pos(sts->get_stream_pos());
		retval->set_value(&(sts->get_value()[pivot + 1]));
		retval->set_length(old_len - (pivot + 1));
	}
	return( retval );
}

int scml_context::define(struct scml_context *sc)
{
	tag_item *kind_ti, *name_ti, *value_ti;
	char *kind_str = 0, *name_str;
	int retval = 0;
	tag_list *tl;
	
	/* Grab our parameters */
	tl = sc->get_lvalues();
	name_ti = find_tag(tl, "name");
	kind_ti = find_tag(tl, "kind");
	value_ti = find_tag(tl, "value");
	if( name_ti && kind_ti && value_ti ) {
		name_str = scml_string::tag_string(name_ti);
		kind_str = scml_string::tag_string(kind_ti);
		if( !strcmp(kind_str, "variable") ) {
			tag_list *dest_tl;
			
			dest_tl = this->lvalues;
			add_tag(dest_tl, name_str, TAG_NONE)->data =
				value_ti->data;
			retval = 1;
		} else if( !strcmp(kind_str, "static-variable") ) {
			tag_list *dest_tl, *par;
			
			dest_tl = this->scope->get_values();
			par = dest_tl->parent;
			dest_tl->parent = 0;
			add_tag(dest_tl, name_str, TAG_NONE)->data =
				value_ti->data;
			dest_tl->parent = par;
			retval = 1;
		} else if( !strcmp(kind_str, "escape" ) ) {
			struct scml_escape *se;
			
			se = new scml_escape;
			se->name = name_str;
			if( (se->value = scml_string::tag_string(value_ti)) )
				this->scope->get_escape_table()->
					add_escape(se);
			else
				delete se;
			retval = 1;
		} else if( !strcmp(kind_str, "tag") ) {
			tag_item *oparams_ti, *rparams_ti;
			struct scml_handler *sh;
			char *value_str;
			
			/* Build a new command with the given parameters */
			value_str = scml_string::tag_string(value_ti);
			oparams_ti = find_tag(tl, "oparams");
			rparams_ti = find_tag(tl, "rparams");
			sh = sht->find_handler(value_str);
			if( value_str && oparams_ti && rparams_ti && sh ) {
				struct scml_cmd_definition *scd;
				
				scd = new scml_cmd_definition;
				scd->set_name(name_str);
				scd->set_handler(sh);
				scd->set_opt_params(oparams_ti->data.
						    tag_data_u.tl);
				scd->set_req_params(rparams_ti->data.
						    tag_data_u.tl);
				this->scope->add_cmd_definition(scd);
				retval = 1;
			} else {
				if( !sh ) {
					scml_alert(this->stream_pos,
						   SAF_ERROR|SAF_RUNTIME,
						   "Couldn't find handler"
						   " function (%s)",
						   value_str);
				}
			}
		} else if( !strcmp(kind_str, "bracket-tag") ) {
			tag_item *oparams_ti, *rparams_ti;
			struct scml_handler *sh;
			char *value_str;
			
			/* Build a bracketed command with the parameters */
			value_str = scml_string::tag_string(value_ti);
			oparams_ti = find_tag(tl, "oparams");
			rparams_ti = find_tag(tl, "rparams");
			sh = sht->find_handler(value_str);
			if( value_str && oparams_ti && rparams_ti && sh ) {
				struct scml_cmd_definition *scd;
				
				scd = new scml_cmd_definition;
				scd->set_name(name_str);
				scd->set_handler(sh);
				scd->set_flags(scd->get_flags() |
					       SCDF_BRACKETED);
				scd->set_opt_params(oparams_ti->data.
						    tag_data_u.tl);
				scd->set_req_params(rparams_ti->data.
						    tag_data_u.tl);
				this->scope->add_cmd_definition(scd);
				retval = 1;
			} else {
				if( !sh ) {
					scml_alert(this->stream_pos,
						   SAF_ERROR|SAF_RUNTIME,
						   "Couldn't find handler"
						   " function (%s)",
						   value_str);
				}
			}
		} else {
			scml_alert(this->stream_pos, SAF_ERROR|SAF_GENERAL,
				   "Unknown kind(%s) in define",
				   kind_str);
		}
	}
	return( retval );
}

int scml_context::undefine(struct scml_context *sc)
{
	tag_item *name_ti;
	char *name_str;
	int retval = 0;
	
	name_ti = find_tag(sc->get_rvalues(), "name");
	if( (name_str = scml_string::tag_string(name_ti)) ) {
		struct scml_escape *se;
		tag_item *ti;
		
		if( find_tag(this->lvalues, name_str) ) {
			rem_tag(this->lvalues, name_str);
		} else if( (ti = find_tag(this->scope->get_values(),
					  name_str)) ) {
			struct scml_cmd_definition *scd;
			
			if( (ti->data.kind == TAG_REF) &&
			    (scd = scml_cmd_definition::
			     ptr(ti->data.tag_data_u.ref)) ) {
				delete scd;
			}
			rem_tag(this->scope->get_values(), name_str);
		} else if( (se = this->scope->get_escape_table()->
			    find_escape(name_str)) ) {
			this->scope->get_escape_table()->rem_escape(name_str);
			delete se;
		}
		retval = 1;
	}
	return( retval );
}

int scml_context::rename(struct scml_context *sc)
{
	char *name_str, *to_str;
	int retval = 0;
	tag_item *ti;
	
	ti = find_tag(sc->get_rvalues(), "name");
	name_str = scml_string::tag_string(ti);
	ti = find_tag(sc->get_rvalues(), "to");
	to_str = scml_string::tag_string(ti);
	if( name_str && to_str ) {
		struct scml_escape *se;
		
		if( (ti = find_tag(this->lvalues, name_str)) ) {
			add_tag(this->lvalues, to_str, TAG_NONE)->data =
				ti->data;
			rem_tag(this->lvalues, name_str);
		} else if( (ti = find_tag(this->scope->get_values(),
					  name_str)) ) {
			tag_data td;
			
			td = ti->data;
			rem_tag(this->scope->get_values(), name_str);
			add_tag(this->scope->get_values(), to_str,
				TAG_NONE)->data = td;
		} else if( (se = this->scope->get_escape_table()->
			    find_escape(name_str)) ) {
			this->scope->get_escape_table()->rem_escape(name_str);
			se->name = to_str;
			this->scope->get_escape_table()->add_escape(se);
		}
		retval = 1;
	}
	return( retval );
}

int scml_context::ifdef(struct scml_context *sc,
			struct scml_token_sequence *sub,
			int defined)
{
	int found = 0, retval = 0;
	tag_item *name_ti;
	char *name_str;
	
	name_ti = find_tag(sc->get_rvalues(), "name");
	if( (name_str = scml_string::tag_string(name_ti)) ) {
		struct scml_token_sequence *else_sts;
		struct scml_scope *cmd_scope;
		
		if( find_tag(this->get_rvalues(), name_str) )
			found = 1;
		else if( find_tag(this->scope->get_values(), name_str) )
			found = 1;
		else if( this->scope->get_escape_table()->
			 find_escape(name_str) )
			found = 1;
		else_sts = this->
			partition_sequence(sc->get_cmd_def(),
					   this->get_scope()->
					   find_cmd_definition(&cmd_scope,
							       "else"),
					   sub);
		if( (found && defined) || (!found && !defined) ) {
			retval = this->format_sequence(sub);
		} else if( else_sts ) {
			retval = this->format_sequence(else_sts);
		} else
			retval = 1;
	} else {
		scml_alert(this->stream_pos, SAF_ERROR|SAF_RUNTIME,
			   "Couldn't get string from 'name' argument");
	}
	return( retval );
}

int scml_context::exec_token(struct scml_token_sequence *sts, int *index,
			     struct scml_token *st)
{
	int retval = 1;
	
	switch( st->kind ) {
	case SCML_NT_COMMAND: {
		struct scml_cmd_definition *scd = 0;
		struct scml_scope *cmd_scope;
		
		switch( st->value.children[0].kind ) {
		case SCML_NT_EXPR: {
			struct scml_token expr;
			
			/* A simple expression, just print it */
			expr = this->eval_token(&st->value.children[0]);
			retval = this->print_token(&expr);
			break;
		}
		case SCML_TERM_SLASH:
			if( !(scd = this->find_cmd(&cmd_scope,
						   &st->value.children[1])) ) {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_RUNTIME,
					   "Couldn't locate command name %s",
					   st->value.children[1].
					   get_identifier());
				retval = 0;
			}
			break;
		case SCML_TERM_ID:
		case SCML_NT_NAME:
			if( !(scd = this->find_cmd(&cmd_scope,
						   &st->value.children[0])) ) {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_RUNTIME,
					   "Couldn't locate command name %s",
					   st->value.children[0].
					   get_identifier());
				retval = 0;
			}
			break;
		default:
			scml_alert(this->stream_pos,
				   SAF_ERROR|SAF_RUNTIME,
				   "Invalid command");
			retval = 0;
			break;
		}
		/* We have to execute a command */
		if( scd ) {
			struct scml_token_sequence *sub = 0;
			struct scml_context *sc;
			
			/* Construct a sub context to execute the command in */
			sc = new scml_context;
			sc->set_flags(this->flags & SCF_IGNORE_WHITE_SPACE);
			sc->set_parent(this);
			sc->set_stream_pos(this->stream_pos);
			sc->set_scope(cmd_scope);
			sc->set_offset_size(this->offset_size);
			sc->set_indent_size(this->indent_size);
			sc->set_ws_queue(this->ws_queue);
			sc->set_cmd_def(scd);
			/* Check for built in commands first */
			if( !strcmp( "define", scd->get_name() ) ) {
				if( sc->handle_params(st) )
					retval = this->define(sc);
				else
					scml_alert(this->stream_pos,
						   SAF_ERROR|SAF_RUNTIME,
						   "Bad args to %s",
						   scd->get_name());
			} else if( !strcmp( "undef", scd->get_name() ) ) {
				if( sc->handle_params(st) )
					retval = this->undefine(sc);
				else
					scml_alert(this->stream_pos,
						   SAF_ERROR|SAF_RUNTIME,
						   "Bad args to %s",
						   scd->get_name());
			} else if( !strcmp( "rename", scd->get_name() ) ) {
				if( sc->handle_params(st) )
					retval = this->rename(sc);
				else
					scml_alert(this->stream_pos,
						   SAF_ERROR|SAF_RUNTIME,
						   "Bad args to %s",
						   scd->get_name());
			} else if( !strcmp( "ifdef", scd->get_name() ) ) {
				if( sc->handle_params(st) &&
				    (sub = this->
				     get_contents(scd, sts,
						  (*index) + 1)) ) {
					(*index) += sub->get_length() + 1;
					retval = this->ifdef(sc, sub, 1);
				}
				else
					scml_alert(this->stream_pos,
						   SAF_ERROR|SAF_RUNTIME,
						   "Bad args to %s",
						   scd->get_name());
			} else if( !strcmp( "ifndef", scd->get_name() ) ) {
				if( sc->handle_params(st) &&
				    (sub = this->
				     get_contents(scd, sts,
						  (*index) + 1)) ) {
					(*index) += sub->get_length() + 1;
					retval = this->ifdef(sc, sub, 0);
				}
				else
					scml_alert(this->stream_pos,
						   SAF_ERROR|SAF_RUNTIME,
						   "Bad args to %s",
						   scd->get_name());
			} else {
				if( scd->get_flags() & SCDF_BRACKETED ) {
					/*
					 * Its bracketed so get the contents
					 * and add a "contents" tag to the
					 * context so the command can use it
					 */
					if( (sub = this->
					     get_contents(scd, sts,
							  (*index) + 1)) ) {
						add_tag(sc->rvalues,
							"contents",
							TAG_REF,
							sub->tag_ref());
						/*
						 * We have to skip over the
						 * contents
						 */
						(*index) += sub->
							get_length() + 1;
						retval = scd->execute(st, sc);
					} else {
						scml_alert(this->stream_pos,
							   SAF_ERROR|
							   SAF_RUNTIME,
							   "Non terminated "
							   "command '%s'",
							   scd->get_name());
						retval = 0;
					}
				} else {
					/* Its a plain command just execute */
					retval = scd->execute(st, sc);
				}
				if( !retval ) {
					scml_alert(this->stream_pos,
						   SAF_ERROR|
						   SAF_RUNTIME,
						   "Command '%s' failed to"
						   " execute",
						   scd->get_name());
				}
			}
			if( sc->flags & SCF_PREFORMATTED )
				this->ws_queue = sc->get_ws_queue();
			else
				this->ws_queue = 0;
			delete sc;
		}
		(*index)++;
		break;
	}
	default:
		retval = this->print_token(st);
		(*index)++;
		break;
	}
	return( retval );
}

int scml_context::format_sequence(struct scml_token_sequence *sts)
{
	struct scml_stream_pos *old_ssp;
	struct scml_token curr;
	int lpc, retval = 1;
	
	old_ssp = this->stream_pos;
	this->stream_pos = sts->get_stream_pos();
	for( lpc = 0; (lpc < sts->get_length()) && retval; ) {
		/* Evaluate and execute the tokens */
		curr = this->eval_token(&(sts->get_value()[lpc]));
		retval = this->exec_token(sts, &lpc, &curr);
	}
	this->stream_pos = old_ssp;
	if( !retval ) {
		printf("Stack Trace -\n");
		this->print(0);
		panic("Too many errors, bailing out");
	}
	return( retval );
}

struct scml_type_map {
	const char *name;
	tag_data_kind tag_type;
};

struct scml_type_map scml_tag_types[] = {
	{"any", TAG_ANY},
	{"int", TAG_INTEGER},
	{"float", TAG_FLOAT},
	{"string", TAG_REF},
	{"tag_list", TAG_TAG_LIST},
	{"bool", TAG_BOOL},
	{"scml", TAG_REF},
	{"cmd", TAG_REF},
	{"internal", TAG_REF},
	{"stream", TAG_REF},
	{0, (tag_data_kind) 0}
};

void scml_context::tag_type(struct scml_token *t_type,
			    tag_data_kind *out_kind, int *out_size)
{
	switch( t_type->kind ) {
	case SCML_NT_SEL: {
		struct scml_token st;
		
		this->tag_type(&t_type->value.children[0], out_kind, out_size);
		if( *out_kind != -1 ) {
			st = this->eval_token(&t_type->value.children[1]);
			st.strip();
			if( st.kind == SCML_TERM_INT ) {
				*out_kind = (tag_data_kind)
					(*out_kind | TAG_ARRAY);
				*out_size = st.value.i;
			} else {
				scml_alert(this->stream_pos,
					   SAF_ERROR|SAF_RUNTIME,
					   "Couldn't get integer for "
					   "array size");
				*out_kind = (tag_data_kind)(-1);
			}
		}
		break;
	}
	case SCML_TERM_ID: {
		int lpc, match = -1;
		
		for( lpc = 0; scml_tag_types[lpc].name && (match == -1);
		     lpc++ ) {
			if( !strcmp( scml_tag_types[lpc].name,
				     t_type->value.id ) )
				match = lpc;
		}
		if( match != -1 ) {
			*out_kind = scml_tag_types[match].tag_type;
		} else {
			*out_kind = (tag_data_kind)(-1);
		}
		break;
	}
	default:
		break;
	}
}

int scml_context::exec_cmd(const char *cmd_name, tag_list *locals, ...)
{
	struct scml_cmd_definition *scd;
	struct scml_stream_pos *ssp = 0;
	struct scml_scope *cmd_scope;
	struct scml_stream *ss = 0;
	tag_data_kind tag_kind;
	struct scml_context *child;
	struct scml_token cmd, id;
	union tag_data_u data;
	va_list arg_addr;
	char *arg_name;
	int retval = 0;
	tag_item *ti;
	
	va_start(arg_addr, locals);
	if( (scd = this->scope->find_cmd_definition(&cmd_scope, cmd_name)) ) {
		/* Build up a fake command to execute */
		id.kind = SCML_TERM_ID;
		id.value.id = cmd_name;
		cmd.kind = SCML_NT_COMMAND;
		cmd.value.children = new scml_token[2];
		cmd.value.children[0] = id;
		cmd.value.children[1].kind = SCML_NONE;
		
		if( !this->stream_pos ) {
			if( scd->get_token_sequence() )
				this->stream_pos = scd->get_token_sequence()->
					get_stream_pos();
			else {
				ss = new scml_stream;
				ss->set_desc(cmd_name);
				ssp = new scml_stream_pos;
				ssp->set_stream(ss);
				this->stream_pos = ssp;
			}
		}
		/*
		 * Construct a child context to execute in.  This is
		 * necessary since we need to replicate the usual
		 * environment for scml code.  A context is executing
		 * the root code and then spawns a child to execute
		 * commands and what not.  And since some of the
		 * commands expect this environment, so who are we
		 * to deny them?
		 */
		child = new scml_context;
		child->set_parent(this);
		child->set_stream_pos(this->stream_pos);
		child->set_cmd_def(scd);
		child->set_scope(cmd_scope);
		/*
		 * Set the parents of the tag lists to be the passed in
		 * locals so that they can reference data in them without
		 * modify the actual tag list by defining variables.
		 */
		child->get_lvalues()->parent = locals;
		this->lvalues->parent = locals;
		/*
		 * Walk through the var args list as if it were the parameter
		 * list in a regular call.
		 */
		arg_name = va_arg(arg_addr, char *);
		while( arg_name ) {
			tag_kind = va_arg(arg_addr, tag_data_kind);
			data = va_arg(arg_addr, union tag_data_u);
			ti = add_tag(child->get_lvalues(),
				     arg_name,
				     TAG_NONE);
			ti->data.kind = tag_kind;
			set_tag_data(&ti->data, 0, data);
			arg_name = va_arg(arg_addr, char *);
		}
		retval = scd->execute(&cmd, child);
		child->get_lvalues()->parent = 0;
		this->lvalues->parent = 0;
		delete child;
	}
	if( ssp )
		delete ssp;
	if( ss )
		delete ss;
	va_end(arg_addr);
	return( retval );
}

void scml_context::set_handler_table(struct scml_handler_table *handlers)
{
	sht = handlers;
}

struct scml_handler_table *scml_context::get_handler_table()
{
	return( sht );
}

void scml_context::print(int level)
{
	union tag_data_u data;

	w_set_fh(stdout);
	printf("SCML Context(%d)\n", level);
	if( this->scope->get_name() )
		printf("  Current Scope: %s\n", this->scope->get_name());
	else
		printf("  Root Scope\n");
	data.tl = this->lvalues;
	printf("  lvalues: ");
	print_tag_data(4, TAG_TAG_LIST, data);
	printf("\n");
	data.tl = this->rvalues;
	printf("  rvalues: ");
	print_tag_data(4, TAG_TAG_LIST, data);
	printf("\n");
	if( this->parent )
		this->parent->print(level + 1);
}

scml_handler_table::scml_handler_table()
{
	int lpc;
	
	for( lpc = 0; lpc < SCML_HANDLER_TABLE_SIZE; lpc++ ) {
		this->table[lpc] = 0;
	}
}

scml_handler_table::~scml_handler_table()
{
}

void scml_handler_table::add_handler(struct scml_handler *sh)
{
	int h;
	
	h = scml_hash_name(sh->name, SCML_HANDLER_TABLE_SIZE);
	sh->next = this->table[h];
	this->table[h] = sh;
}

struct scml_handler *scml_handler_table::find_handler(const char *name)
{
	struct scml_handler *retval = 0, *curr;
	int h;
	
	if( name ) {
		h = scml_hash_name(name, SCML_HANDLER_TABLE_SIZE);
		curr = this->table[h];
		while( curr && !retval ) {
			if( !strcmp( name, curr->name ) )
				retval = curr;
			curr = curr->next;
		}
	}
	return( retval );
}

int c_defvar_handler(struct scml_token *st, struct scml_context *sc)
{
	struct scml_token expr;
	int lpc, retval = 1;
	
	/*
	 * We diverge from regular syntax and just process the parameters
	 * ourselves, turning them into variables for the parent context.
	 */
	for( lpc = 1; st->value.children[lpc].kind != SCML_NONE; lpc++ ) {
		expr = sc->get_parent()->eval_token(&st->value.children[lpc]);
		if( expr.kind == SCML_TERM_TAG ) {
			if( find_tag(sc->get_parent()->get_lvalues(),
				     expr.value.ti->tag) ) {
				scml_alert(sc->get_stream_pos(),
					   SAF_ERROR|SAF_GENERAL,
					   "Tag \"%s\" has already been "
					   "defined", expr.value.ti->tag);
				retval = 0;
			} else {
				add_tag(sc->get_parent()->get_lvalues(),
					expr.value.ti->tag,
					TAG_NONE)->data = expr.value.ti->data;
				delete_tag_item(expr.value.ti);
			}
		} else {
			scml_alert(sc->get_stream_pos(), SAF_ERROR|SAF_GENERAL,
				   "Expression used in defvar doesn't"
				   "result in a tag");
			retval = 0;
		}
	}
	return( retval );
}

int c_ignore_handler(struct scml_token *st, struct scml_context *sc)
{
	int lpc;
	
	/* Similar to defvar except we don't change anything */
	for( lpc = 1; st->value.children[lpc].kind != SCML_NONE; lpc++ ) {
		sc->get_parent()->eval_token(&st->value.children[lpc]);
	}
	return( 1 );
}

int c_scope_handler(struct scml_token *st, struct scml_context *sc)
{
	int retval = 0;
	
	if( sc->handle_params(st) ) {
		struct scml_token_sequence *sts;
		struct scml_scope *scope;
		char *sc_name;
		tag_item *ti;
		
		if( (sc_name = scml_string::
		     tag_string(find_tag(sc->get_rvalues(), "name"))) ) {
			if( strlen(sc_name) ) {
				if( !(scope = sc->get_parent()->
				      get_scope()->find_child(sc_name)) ) {
					/*
					 * Construct a new scope if one isn't
					 * already made.
					 */
					scope = new scml_scope;
					scope->set_name(sc_name);
					scope->set_escape_table(
						new scml_escape_table);
					sc->get_parent()->get_scope()->
						add_child(scope);
				}
			} else {
				/* Empty string means goto the root scope */
				scope = sc->get_parent()->get_scope();
				while( scope->get_parent() )
					scope = scope->get_parent();
			}
			/*
			 * We need to change the parent to this scope and then
			 * have it execute our sequence since we're just
			 * changing the lexical scope and not changing
			 * the execution context.  (i.e. we still need
			 * to get access to any local variables and what not)
			 */
			sc->get_parent()->set_scope(scope);
			if( sc->get_parent()->get_flags() &
			    SCF_PREFORMATTED ) {
				sc->print_indent(sc->get_ws_queue());
				sc->set_ws_queue(0);
				sc->get_parent()->set_ws_queue(0);
			}
			sc->get_parent()->
				set_flags(sc->get_parent()->get_flags() &
					  ~(SCF_PRINTING_BODY));
			ti = find_tag(sc->get_rvalues(), "contents");
			if( ti && (ti->data.kind == TAG_REF) &&
			    (sts = scml_token_sequence::
			     ptr(ti->data.tag_data_u.ref)) ) {
				retval = sc->get_parent()->
					format_sequence(sts);
			}
			sc->set_ws_queue(sc->get_parent()->get_ws_queue());
			sc->get_parent()->set_scope(scope->get_parent());
		} else {
			scml_alert(sc->get_stream_pos(), SAF_ERROR|SAF_RUNTIME,
				   "Invalid string passed in name tag"
				   "for scope");
		}
	}
	return( retval );
}

/*
 * This is the handler for the macro command, it will construct the macro
 * from its parameters and contents.
 */
int c_macro_handler(struct scml_token *st, struct scml_context *sc)
{
	struct scml_cmd_definition *scd;
	struct scml_scope *cmd_scope;
	char *name_str;
	int retval = 0;
	
	if( !sc->handle_params(st) )
		return( retval );
	if( (name_str = scml_string::
	     tag_string(find_tag(sc->get_rvalues(), "name"))) &&
	    !sc->get_parent()->get_scope()->
	    find_cmd_definition(&cmd_scope, name_str) &&
	    find_tag(sc->get_rvalues(), "contents")) {
		/* Build the new command from the given parameters */
		scd = new scml_cmd_definition;
		scd->set_name(name_str);
		if( find_tag(sc->get_rvalues(), "close")->data.tag_data_u.b ) {
			scd->set_flags(scd->get_flags() | SCDF_BRACKETED);
		}
		/*
		 * We use a special handler to actually execute the
		 * macro since all commands need a handler to actually
		 * do something.
		 */
		scd->set_handler(scml_context::get_handler_table()->
				 find_handler("c-macro-executer"));
		scd->set_token_sequence(scml_token_sequence::
					ptr(find_tag(sc->get_rvalues(),
						     "contents")->
					    data.tag_data_u.ref));
		scd->set_opt_params(find_tag(sc->get_rvalues(),
					     "oparams")->
				    data.tag_data_u.tl);
		scd->set_req_params(find_tag(sc->get_rvalues(),
					     "rparams")->
				    data.tag_data_u.tl);
		sc->get_parent()->get_scope()->add_cmd_definition(scd);
		retval = 1;
	} else if( !name_str ) {
		scml_alert(sc->get_stream_pos(), SAF_ERROR|SAF_RUNTIME,
			   "Invalid string passed in name tag for macro");
	} else {
		scml_alert(sc->get_stream_pos(), SAF_ERROR|SAF_RUNTIME,
			   "A macro named %s has already been defined",
			   name_str);
	}
	return( retval );
}

/*
 * This is the handler for executing user macros.  This is different from
 * the above handler since it only constructed the command for the macro
 * this does the actual execution.
 */
int c_macro_executer(struct scml_token *st, struct scml_context *sc)
{
	int retval = 0;
	
	if( sc->handle_params(st) ) {
		struct scml_token_sequence *sts;
		int old_offset, old_indent;
		
		/* Setup the context with values from the sequence */
		sts = sc->get_cmd_def()->get_token_sequence();
		old_offset = sc->get_offset_size();
		sc->set_offset_size(sts->get_indent());
		old_indent = sc->get_indent_size();
		/*
		 * If the parent was preformatted than it will have
		 * indenting information that we need to duplicate.  This
		 * will record the position of the macro call so that
		 * it can be used when printing to get the same initial
		 * indentation as well as any indentation in the macro.
		 */
		if( sc->get_parent()->get_flags() & SCF_PREFORMATTED )
			sc->set_indent_size(sc->get_stream_pos()->
					    get_column() - old_offset);
		/*
		 * Finally execute the macro, note that the format_sequence
		 * that called up has already gotten our "contents" variable
		 * set up.
		 */
		if( !(retval = sc->format_sequence(sts)) )
			scml_alert(sc->get_stream_pos(), SAF_ERROR|SAF_RUNTIME,
				   "There was an error in the macro body");
		sc->set_indent_size(old_indent);
		sc->set_offset_size(old_offset);
	}
	return( retval );
}

/* This handles the if command */
int c_if_handler(struct scml_token *st, struct scml_context *sc)
{
	struct scml_token expr;
	int retval = 0;
	
	/*
	 * Similar to defvar we cheat and don't follow the syntax,
	 * the first child in the token is expected to just evaluate
	 * to an int or bool, rather than setting a parameter.
	 */
	expr = sc->get_parent()->eval_token(&st->value.children[1]);
	expr.strip();
	if( ((expr.kind == SCML_TERM_INT) || (expr.kind == SCML_TERM_BOOL)) ) {
		struct scml_token_sequence *sts, *else_sts;
		struct scml_scope *cmd_scope;
		tag_item *ti;
		
		ti = find_tag(sc->get_rvalues(), "contents");
		if( ti && (ti->data.kind == TAG_REF) &&
		    (sts = scml_token_sequence::
		     ptr(ti->data.tag_data_u.ref)) ) {
			/* Try and partition around an <else> */
			sc->set_flags(sc->get_parent()->get_flags());
			else_sts = sc->partition_sequence(
				sc->get_cmd_def(),
				sc->get_scope()->
				find_cmd_definition(&cmd_scope, "else"),
				sts);
			if( sc->get_parent()->get_flags() &
			    SCF_PREFORMATTED ) {
				sc->print_indent(sc->get_ws_queue());
				sc->set_ws_queue(0);
				sc->get_parent()->set_ws_queue(0);
			}
			sc->get_parent()->
				set_flags(sc->get_parent()->get_flags() &
					  ~SCF_PRINTING_BODY);
			/* Finally, do the test and execute the code. */
			if( expr.value.i ) {
				retval = sc->get_parent()->
					format_sequence(sts);
			} else if( else_sts ) {
				retval = sc->get_parent()->
					format_sequence(else_sts);
			} else
				retval = 1;
			sc->set_ws_queue(sc->get_parent()->get_ws_queue());
		}
	} else {
		scml_alert(sc->get_stream_pos(), SAF_ERROR|SAF_RUNTIME,
			   "Non integer or boolean used for if");
		expr.print();
	}
	return( retval );
}

/*
 * This handles the else command.  Since its sole purpose in live is to be
 * used as something to partition around we just throw an error.
 */
int c_else_handler(struct scml_token */*st*/, struct scml_context *sc)
{
	scml_alert(sc->get_stream_pos(), SAF_ERROR|SAF_RUNTIME,
		   "Else should never be called by itself, it needs"
		   " to be contained in an if block");
	return( 0 );
}

/*
 * This is the handler for 'for'.  It just a simple loop construct that
 * can iterate over a tag array.
 */
int c_for_handler(struct scml_token *st, struct scml_context *sc)
{
	int retval = 0;
	
	if( sc->handle_params(st) ) {
		struct scml_token_sequence *sts;
		union tag_data_u data;
		char *iter_name, *each_str;
		tag_item *tag, *iter_ti, *ti;
		int len;
		
		/* Grab the iterator and tag to iterate over */
		iter_name = scml_string::
			tag_string(find_tag(sc->get_rvalues(), "iter"));
		if( (each_str = scml_string::
		     tag_string(find_tag(sc->get_rvalues(), "each"))) &&
		    iter_name ) {
			tag = find_tag(sc->get_parent()->get_rvalues(),
				       each_str);
			if( !tag && strlen(each_str) ) {
				scml_alert(sc->get_stream_pos(),
					   SAF_ERROR|SAF_RUNTIME,
					   "Couldn't find tag '%s' for "
					   "'for' loop", each_str);
				return 0;
			}
			/* We can iterate over an array or a specific length */
			len = find_tag(sc->get_rvalues(), "length")->
				data.tag_data_u.i;
			if( tag ) {
				len = tag_data_length(&tag->data);
			}
			/* Add the iterator tag */
			if( !(iter_ti = find_tag(sc->get_parent()->
						 get_lvalues(),
						 iter_name)) ) {
				iter_ti = add_tag(sc->get_parent()->
						  get_lvalues(),
						  iter_name, TAG_INTEGER, 0);
			}
			ti = find_tag(sc->get_rvalues(), "contents");
			if( iter_ti->data.kind != TAG_INTEGER ) {
				scml_alert(sc->get_stream_pos(),
					   SAF_ERROR|SAF_RUNTIME,
					   "Iterator '%s' already exists"
					   " and is not an integer",
					   iter_name);
			} else if( ti && (ti->data.kind == TAG_REF) &&
				   (sts = scml_token_sequence::
				    ptr(ti->data.tag_data_u.ref)) ) {
				int lpc;
				
				/* Finally, Loop over the contents */
				retval = 1;
				for( lpc = 0; (lpc < len) && retval; lpc++ ) {
					data.i = lpc;
					set_tag_data(&find_tag(sc->
							       get_parent()->
							       get_rvalues(),
							       iter_name)->
						     data,
						     0,
						     data);
					retval = sc->get_parent()->
						format_sequence(sts);
				}
			}
		} else {
			scml_alert(sc->get_stream_pos(), SAF_ERROR|SAF_RUNTIME,
				   "Invalid string passed in tags for 'for'");
		}
	}
	return( retval );
}

/*
 * This is the handler for the preformatting tag, it just sets the flag
 * for preformatting in the context
 */
int c_pre_handler(struct scml_token *st, struct scml_context *sc)
{
	int retval = 0;
	
	if( sc->handle_params(st) ) {
		struct scml_token_sequence *sts;
		tag_item *ti;
		int do_indent;
		
		/* Clear any queued white space */
		sc->set_ws_queue(0);
		sc->get_parent()->set_ws_queue(0);
		sc->get_parent()->set_flags((sc->get_flags() |
					     SCF_PREFORMATTED));
		/*
		 * Check if we're supposed to do extra indenting based
		 * on where a command is called
		 */
		do_indent = find_tag(sc->get_rvalues(), "indented")->data.
			tag_data_u.b;
		if( !do_indent )
			sc->get_parent()->set_flags(sc->get_parent()->
						    get_flags() |
						    SCF_IGNORE_INDENT);
		ti = find_tag(sc->get_rvalues(), "contents");
		if( ti && (ti->data.kind == TAG_REF) &&
		    (sts = scml_token_sequence::
		     ptr(ti->data.tag_data_u.ref)) ) {
			int old_offset;
			
			/* Set the parent to our sequences offset */
			old_offset = sc->get_parent()->get_offset_size();
			sc->get_parent()->set_offset_size(sts->get_indent());
			if( !(retval = sc->get_parent()->
			      format_sequence(sts)) )
				scml_alert(sc->get_stream_pos(),
					   SAF_ERROR|SAF_RUNTIME,
					   "There was an error in the "
					   "preformat body");
			sc->set_ws_queue(sc->get_parent()->get_ws_queue());
			sc->get_parent()->set_offset_size(old_offset);
		}
		sc->get_parent()->set_flags(sc->get_flags());
	}
	return( retval );
}

/*
 * This is the handler for aliascmd, it simply creates a new command definition
 * that is an alias for another.
 */
int c_aliascmd_handler(struct scml_token *st, struct scml_context *sc)
{
	int retval = 0;
	
	if( sc->handle_params(st) ) {
		struct scml_cmd_definition *scd = 0;
		struct scml_scope *cmd_scope;
		char *name_str;
		
		name_str = scml_string::tag_string(find_tag(sc->get_rvalues(),
							    "name"));
		if( name_str &&
		    (scd = scml_cmd_definition::ptr(find_tag(sc->get_rvalues(),
							     "handler")->
						    data.tag_data_u.ref)) &&
		    !sc->get_parent()->get_scope()->
		    find_cmd_definition(&cmd_scope, name_str) ) {
			struct scml_cmd_definition *new_scd;
			
			/* Make a new command def based on the specified one */
			new_scd = new scml_cmd_definition;
			new_scd->set_name(name_str);
			new_scd->set_flags(scd->get_flags());
			new_scd->set_handler(scd->get_handler());
			new_scd->set_token_sequence(scd->get_token_sequence());
			new_scd->set_opt_params(scd->get_opt_params());
			new_scd->set_req_params(scd->get_req_params());
			sc->get_parent()->get_scope()->
				add_cmd_definition(new_scd);
			retval = 1;
		} else {
			if( !scd ) {
				scml_alert(sc->get_stream_pos(),
					   SAF_ERROR|SAF_RUNTIME,
					   "Couldn't resolve cmd_name value"
					   " to a command");
			}
			if( !name_str ) {
				scml_alert(sc->get_stream_pos(),
					   SAF_ERROR|SAF_RUNTIME,
					   "Invalid value for 'name' tag");
			}
		}
	}
	return( retval );
}

/* This is handler for include, it allows you to include other scml files. */
int c_include_handler(struct scml_token *st, struct scml_context *sc)
{
	char *filename;
	int retval = 0;
	
	if( !sc->handle_params(st) )
		return 0;
	if( (filename = scml_string::tag_string(find_tag(sc->get_rvalues(),
							 "file"))) ) {
		const char * const *include_dir_list;
		FILE *file;
		char *found_filename;
		
		/* Try and open the file in a set of include dirs */
		include_dir_list = sc->get_stream_pos()->get_stream()->
				   get_include_directory_list();
		file = fopen_search(filename, include_dir_list, "r",
				    &found_filename);
		if (file) {
			struct scml_token_sequence *sts;
			struct scml_stream_pos *ssp;
			struct scml_scope *scope;
			struct scml_stream *ss;
			struct scml_parser *sp;
			
			/* Read in, parse, and execute the file */
			scope = sc->get_parent()->get_scope();
			ss = new scml_stream();
			ss->set_file(file);
			ss->set_desc(found_filename);
			ss->set_include_directory_list(include_dir_list);
			ssp = new scml_stream_pos();
			ssp->set_stream(ss);
			sp = new scml_parser;
			sp->set_stream_pos(ssp);
			if( (sts = sp->parse()) ) {
				struct scml_context *new_sc;
				
				new_sc = new scml_context;
				new_sc->set_flags(new_sc->get_flags() |
						  SCF_IGNORE_WHITE_SPACE);
				new_sc->set_stream_pos(ssp);
				new_sc->set_scope(scope);
				if( new_sc->format_sequence(sts) ) {
					retval = 1;
				} else {
					scml_alert(new_sc->get_stream_pos(),
						   SAF_ERROR|SAF_RUNTIME,
						   "There was an error in"
						   "executing the include "
						   "file '%s'", filename);
				}
			} else {
				scml_alert(ssp,
					   SAF_ERROR|SAF_RUNTIME,
					   "There was an error parsing "
					   "include file '%s'", filename);
			}
		} else {
			scml_alert(sc->get_stream_pos(),
				   SAF_ERROR|SAF_RUNTIME,
				   "Cannot open include file '%s'",
				   filename);
		}
	} else {
		scml_alert(sc->get_stream_pos(),
			   SAF_ERROR|SAF_RUNTIME,
			   "Unable to get string from 'file' argument");
	}
	return( retval );
}

int c_retarget_handler(struct scml_token *st, struct scml_context *sc)
{
	int retval = 0;
	
	if( sc->handle_params(st) ) {
		struct scml_stream *ss;
		tag_item *ti;
		
		ti = find_tag(sc->get_rvalues(), "output");
		if( (ss = scml_stream::ptr(ti->data.tag_data_u.ref)) ) {
			if( ss->get_file() ) {
				sc->get_parent()->set_flags(
					sc->get_parent()->get_flags() &
					~SCF_IGNORE_WHITE_SPACE);
				w_set_fh(ss->get_file());
			} else {
				sc->get_parent()->set_flags(
					sc->get_parent()->get_flags() |
					SCF_IGNORE_WHITE_SPACE);
				w_set_fh(stdout);
			}
			retval = 1;
		} else {
			scml_alert(sc->get_stream_pos(),
				   SAF_ERROR|SAF_RUNTIME,
				   "Cannot get output stream");
		}
	}
	return( retval );
}

int c_create_stream_handler(struct scml_token *st, struct scml_context *sc)
{
	int retval = 0;
	
	if( sc->handle_params(st) ) {
		char *name_str, *path_str;
		tag_item *ti;
		
		ti = find_tag(sc->get_rvalues(), "name");
		name_str = scml_string::tag_string(ti);
		ti = find_tag(sc->get_rvalues(), "path");
		path_str = scml_string::tag_string(ti);
		if( name_str && path_str ) {
			struct scml_stream *ss;
			FILE *file = NULL;
			
			if( strlen(path_str) ) {
				file = fopen(path_str, "w");
				if( !file )
					panic("Cannot create file %s",
					      path_str);
			}
			ss = new scml_stream;
			ss->set_flags((ss->get_flags() | SSF_OUTPUT) &
				      ~SSF_INPUT);
			ss->set_desc(path_str);
			ss->set_file(file);
			add_tag(sc->get_scope()->get_values(), name_str,
				TAG_REF, ss->tag_ref());
			retval = 1;
		}
	}
	return( retval );
}

struct cast_handler_entry scml_code_cast_handler_entry;

int init_scml()
{
	struct scml_handler_table *sht;
	struct scml_handler *sh;
	
	scml_code_cast_handler_entry.he.name = "scml-code-cast-handler";
	scml_code_cast_handler_entry.c_func = scml_code_cast_handler;
	add_entry(cast_handler_table, &scml_code_cast_handler_entry.he);
	
	/* Construct a handler table for the set of built in handlers */
	sht = new scml_handler_table;
	
	sh = new scml_handler;
	sh->name = "c-ignore-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_ignore_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-macro-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_macro_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-macro-executer";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_macro_executer;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-scope-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_scope_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-if-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_if_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-defvar-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_defvar_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-else-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_else_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-for-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_for_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-pre-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_pre_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-aliascmd-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_aliascmd_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-include-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_include_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-retarget-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_retarget_handler;
	sht->add_handler(sh);
	
	sh = new scml_handler;
	sh->name = "c-create-stream-handler";
	sh->kind = SHK_C_FUNCTION;
	sh->function.c_func = c_create_stream_handler;
	sht->add_handler(sh);
	
	scml_context::set_handler_table(sht);
	
	return( 1 );
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1