/*
 * 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 <string.h>
#include <ctype.h>

#include <mom/c/scml.hh>

static const char* scml_std_include_dirs_array[]
	= {
		".",
		SCML_INSTALL_DIR,	/* Set by Makefile. */
		0			/* Terminating null string. */
	};

const char **scml_std_include_dirs()
{
	return scml_std_include_dirs_array;
}

void scml_alert(struct scml_stream_pos *ssp,
		int alert_flags,
		const char *format, ...)
{
	const char *alert_type = 0;
	const char *alert_class = 0;
	va_list args;
	
	va_start( args, format );
	switch( alert_flags & SAF_ALERT_CLASS ) {
	case SAF_INTERNAL:
		alert_class = "Internal";
		break;
	case SAF_GENERAL:
		alert_class = "General";
		break;
	case SAF_IO:
		alert_class = "I/O";
		break;
	case SAF_LEXICAL:
		alert_class = "Lexical";
		break;
	case SAF_PARSE:
		alert_class = "Parse";
		break;
	case SAF_TYPE:
		alert_class = "Type";
		break;
	case SAF_RUNTIME:
		alert_class = "Runtime";
		break;
	default:
		panic("Invalid alert class 0x%x passed to scml_alert()",
		      alert_flags & SAF_ALERT_CLASS);
		break;
	}
	switch( alert_flags & SAF_ALERT_TYPE ) {
	case SAF_WARNING:
		alert_type = "Warning";
		break;
	case SAF_ERROR:
		alert_type = "Error";
		break;
	default:
		panic("Invalid alert type 0x%x passed to scml_alert()",
		      alert_flags & SAF_ALERT_TYPE);
		break;
	}
	if( ssp ) {
		fprintf(stderr, "%s %s %s[",
			alert_class, alert_type,
			ssp->get_stream()->get_desc());
		if( ssp->get_last_row() == -1 ) {
			fprintf(stderr, "%d:%d] - ",
				ssp->get_row(), ssp->get_column());
		} else {
			/*
			 * This will print out a range to make it easier
			 * for the user to find the problem
			 */
			fprintf(stderr, "%d:%d - %d:%d] - ",
				ssp->get_last_row(), ssp->get_last_column(),
				ssp->get_row(), ssp->get_column());
		}
	}
	else {
		fprintf(stderr, "%s %s - ", alert_class, alert_type);
	}
	vfprintf(stderr, format, args);
	fprintf(stderr, "\n");
	va_end( args );
}

int scml_hash_name(const char *name, int table_size)
{
	int h;
	
	for( h = 0; *name; name++ )
		h = (64 * h + tolower(*name)) % table_size;
	return( h );
}

scml_string::scml_string()
{
	this->components.val = 0;
	this->components.len = 0;
}

scml_string::~scml_string()
{
	free( this->components.val );
}

char *scml_string::tag_ref()
{
	return( ptr_to_tag_ref("struct scml_string *", this) );
}

struct scml_string *scml_string::ptr(char *ref)
{
	return( (struct scml_string *)
		tag_ref_to_ptr("struct scml_string *", ref) );
}

char *scml_string::tag_string(tag_item *ti)
{
	char *retval = 0;
	
	switch( ti->data.kind ) {
	case TAG_STRING:
		retval = ti->data.tag_data_u.str;
		break;
	case TAG_REF:
		retval = scml_string::ptr(ti->data.tag_data_u.ref)->
			make_chars();
		break;
	default:
		break;
	}
	return( retval );
}

tag_data *scml_string::add_component()
{
	tag_data *retval;
	int i;
	
	i = this->components.len++;
	this->components.val = (tag_data *)mustrealloc(this->components.val,
						       sizeof( tag_data ) *
						       this->components.len);
	this->components.val[i].kind = TAG_NONE;
	retval = &this->components.val[i];
	return( retval );
}

void scml_string::concat(struct scml_string *ss)
{
	int start, lpc;
	
	start = this->components.len;
	this->components.len += ss->components.len;
	this->components.val = (tag_data *)mustrealloc(this->components.val,
						       sizeof( tag_data ) *
						       this->components.len);
	for( lpc = 0; lpc < ss->components.len; lpc++ ) {
		this->components.val[start + lpc] = ss->components.val[lpc];
	}
}

int scml_string::cmp(struct scml_string *ss)
{
	int my_pos = 0, ss_pos = 0, done = 0;
	const char *my_str;
	const char *ss_str;
	int retval = 0;
	
	my_str = "";
	ss_str = "";
	while( !done ) {
		/* Find the next non-empty string in the scml_strings */
		while( !(*my_str) && (my_pos < this->components.len) &&
		       (this->components.val[my_pos].kind == TAG_STRING) ) {
			my_str = this->components.val[my_pos].tag_data_u.str;
			my_pos++;
		}
		while( !(*ss_str) && (ss_pos < ss->components.len) &&
		       (ss->components.val[ss_pos].kind == TAG_STRING) ) {
			ss_str = ss->components.val[ss_pos].tag_data_u.str;
			ss_pos++;
		}
		/* Compare the character strings */
		while( *my_str && *ss_str && (*my_str == *ss_str) ) {
			my_str++;
			ss_str++;
		}
		if( *my_str < *ss_str )
			retval = -1;
		else if( *my_str > *ss_str )
			retval = 1;
		/*
		 * Only quit if the characters mismatch or there is nothing
		 * left in the strings
		 */
		if( retval || (!*my_str && !*ss_str) ) {
			done = 1;
		}
	}
	return( retval );
}

char *scml_string::make_chars()
{
	char *retval = 0, *curr;
	int lpc, poss = 1, str_size = 0;
	
	/* Get the size of the string to be produced */
	for( lpc = 0; poss && (lpc < this->components.len); lpc++ ) {
		switch( this->components.val[lpc].kind ) {
		case TAG_BOOL:
			str_size += this->components.val[lpc].
				tag_data_u.b ? 4 : 5;
			break;
		case TAG_STRING:
			str_size += strlen(this->components.val[lpc].
					    tag_data_u.str);
			break;
		case TAG_INTEGER: {
			int i;
			
			i = this->components.val[lpc].tag_data_u.i;
			str_size++;
			while( i ) {
				i /= 10;
				str_size++;
			}
			break;
		}
		case TAG_FLOAT: {
			char f[30];
			
			sprintf(f, "%f", this->components.val[lpc].
				tag_data_u.f);
			str_size += strlen(f);
			break;
		}
		default:
			poss = 0;
			break;
		}
	}
	if( poss ) {
		/* Construct the string */
		retval = (char *)mustmalloc(str_size + 1);
		curr = retval;
		for( lpc = 0; lpc < this->components.len; lpc++ ) {
			switch( this->components.val[lpc].kind ) {
			case TAG_BOOL:
				strcpy(curr,
				       this->components.val[lpc].
				       tag_data_u.b ? "true" : "false");
				break;
			case TAG_STRING:
				strcpy(curr,
				       this->components.val[lpc].
				       tag_data_u.str);
				break;
			case TAG_INTEGER:
				sprintf(curr, "%d", this->components.
					val[lpc].tag_data_u.i);
				break;
			case TAG_FLOAT:
				sprintf(curr, "%f", this->components.
					val[lpc].tag_data_u.f);
				break;
			default:
				break;
			}
			curr += strlen(curr);
		}
	}
	return( retval );
}

void scml_string::print(int indent)
{
	union tag_data_u data;
	int lpc;
	
	/* Simply walk through the components and use the tag printer */
	for( lpc = 0; lpc < this->components.len; lpc++ ) {
		data = get_tag_data(&this->components.val[lpc], 0);
		print_tag_data(indent, this->components.val[lpc].kind, data);
		indent = 0;
	}
}

int scml_parse_cmdline_defines(struct scml_scope *root_scope,
			       flag_value_seq *defs)
{
	const char *str, *var_value = 0;
	struct scml_token_sequence *sts;
	struct scml_stream_pos *ssp;
	struct scml_stream *ss;
	struct scml_parser *sp;
	unsigned int lpc;
	const char *arg;
	int retval = 0;
	char *var_name;
	int name_len;
	tag_data td;
	
	for( lpc = 0; lpc < defs->len; lpc++ ) {
		arg = defs->values[lpc].string;
		td = create_tag_data(TAG_BOOL, 0);
		td.tag_data_u.b = 1;
		for( str = arg; *str && (*str != '='); str++ );
		if( *str == '=' ) {
			name_len = str - arg;
			var_name = (char *)mustmalloc(name_len + 1);
			strncpy(var_name, arg, name_len);
			var_name[name_len] = 0;
			
			var_value = str + 1;
			ss = new scml_stream;
			ss->set_data(muststrdup(var_value));
			ss->set_desc(flick_asprintf(
				"Command line define `%s'=`%s'",
				var_name, var_value));
			ssp = new scml_stream_pos;
			ssp->set_stream(ss);
			ssp->set_flags(ssp->get_flags() |
				       SSPF_IGNORE_EOF |
				       SSPF_SINGLE_STATE);
			ssp->set_state(SSPS_EXPR);
			sp = new scml_parser;
			sp->set_stream_pos(ssp);
			if( (sts = sp->parse()) &&
			    (sts->get_length() == 1) ) {
				struct scml_context *sc;
				struct scml_token st;
				
				sc = new scml_context;
				sc->set_stream_pos(ssp);
				sc->set_scope(root_scope);
				st = sc->eval_token(sts->get_value());
				switch( st.kind ) {
				case SCML_TERM_BOOL:
					td.kind = TAG_BOOL;
					td.tag_data_u.b = st.value.b;
					retval = 1;
					break;
				case SCML_TERM_INT:
					td.kind = TAG_INTEGER;
					td.tag_data_u.i = st.value.i;
					retval = 1;
					break;
				case SCML_TERM_FLOAT:
					td.kind = TAG_FLOAT;
					td.tag_data_u.f = st.value.f;
					retval = 1;
					break;
				case SCML_TERM_STRING:
					td.kind = TAG_REF;
					td.tag_data_u.ref = st.value.str->
						tag_ref();
					retval = 1;
					break;
				case SCML_TERM_TAG:
					td = st.value.ti->data;
					retval = 1;
					break;
				case SCML_TERM_TAG_LIST:
					td.kind = TAG_TAG_LIST;
					td.tag_data_u.tl = st.value.tl;
					retval = 1;
					break;
				default:
					break;
				}
				delete sc;
			}
			delete sp;
			delete ssp;
			delete ss;
		} else {
			var_name = muststrdup(arg);
			name_len = str - arg;
			retval = 1;
		}
		add_tag(root_scope->get_values(), var_name, TAG_NONE)->
			data = td;
	}
	return( retval );
}

int scml_execute_str(struct scml_scope *root_scope,
		     char *code_desc, char *code, tag_list *tl)
{
	struct scml_token_sequence *sts;
	struct scml_stream_pos *ssp;
	struct scml_parser *sp;
	struct scml_stream *ss;
	int retval = 0;
	
	ss = new scml_stream;
	ss->set_desc(code_desc);
	ss->set_data(muststrdup(code));
	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 *sc;
		
		sc = new scml_context;
		sc->set_stream_pos(ssp);
		sc->set_scope(root_scope);
		sc->get_lvalues()->parent = tl;
		sc->format_sequence(sts);
		delete sc;
		retval = 1;
	}
	delete sp;
	delete ssp;
	delete ss;
	return( retval );
}

int scml_code_cast_handler(int indent, cast_handler *ch)
{
	struct scml_token_sequence *sts;
	struct scml_scope *root_scope;
	struct scml_stream_pos *ssp;
	struct scml_stream *ss;
	struct scml_parser *sp;
	int retval = 0;
	tag_list *tl;
	
	ss = new scml_stream;
	ss->set_desc(ch->args.args_val[0]);
	ss->set_data(muststrdup(ch->args.args_val[1]));
	root_scope = (struct scml_scope *)tag_ref_to_ptr("struct scml_scope *",
							 ch->args.args_val[2]);
	tl = (tag_list *)tag_ref_to_ptr("tag_list *", ch->args.args_val[3]);
	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 *sc;
		
		sc = new scml_context;
		sc->set_stream_pos(ssp);
		sc->set_scope(root_scope);
		sc->set_indent_size(indent * 2);
		sc->get_lvalues()->parent = tl;
		sc->format_sequence(sts);
		delete sc;
		retval = 1;
	}
	delete sp;
	delete ssp;
	delete ss;
	return( retval );
}

struct scml_stream_pos *scml_execute_defs_file(struct scml_scope *root_scope,
					       const char **include_dirs,
					       const char *file_desc,
					       const char *file_name)
{
	struct scml_stream_pos *retval = 0;
	char *found_filename;
	FILE *file;
	
	file = fopen_search(file_name,
			    include_dirs,
			    "r",
			    &found_filename);
	if( file ) {
		struct scml_token_sequence *sts;
		struct scml_stream_pos *ssp;
		struct scml_stream *ss;
		struct scml_parser *sp;
		
		ss = new scml_stream;
		ss->set_desc(file_desc);
		ss->set_file(file);
		ss->set_include_directory_list(include_dirs);
		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 *sc;
			
			sc = new scml_context;
			sc->set_flags(sc->get_flags() |
				      SCF_IGNORE_WHITE_SPACE);
			sc->set_stream_pos(ssp);
			sc->set_scope(root_scope);
			if( sc->format_sequence(sts) ) {
				retval = ssp;
			} else {
				panic("There was an error in executing the"
				      " file `%s'", file_desc);
			}
			delete sc;
		} else {
			panic("Couldn't parse the input file `%s'", file_desc);
		}
		fclose(file);
		delete sp;
	}
	return( retval );
}


syntax highlighted by Code2HTML, v. 0.9.1