/* Copyright (C) 2001 Michael Leonhard
 * Mike Leonhard
 * mike at tamale dot net
 * http://tamale.net/
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "ascorbic.h"

struct Particle *Parse_Assign( struct Ascorbic *asc );
struct Particle *Parse_Block( struct Ascorbic *asc );
int Parse_CreateTree( struct Ascorbic *asc );
struct Particle *Parse_Definition( struct Ascorbic *asc );
struct Particle *Parse_Expression( struct Ascorbic *asc, int prevprecedence );
struct Particle *Parse_Generic( struct Ascorbic *asc, struct Particle *left, int product );
struct Particle *Parse_Identifier( struct Ascorbic *asc );
struct Particle *Parse_Parens( struct Ascorbic *asc );
int Parse_Precedence( int type );
struct Particle *Parse_Ret( struct Ascorbic *asc );
struct Particle *Parse_Statement( struct Ascorbic *asc );
struct Particle *Parse_Sub( struct Ascorbic *asc );

struct Particle *Parse_Assign( struct Ascorbic *asc ) {
	struct Particle *particle, *var, *expr;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	
	/* identifier does not exist */
	if( asc->i >= asc->list->childnum || asc->list->child[ asc->i ]->type != identifier ) {
		Ascorbic_Error( asc, "expected identifier" );
		return NULL;
		}
	
	/* identifier */
	var = asc->list->child[ asc->i ];
	asc->i++;
	
	/* assignsymbol does not exist */
	if( asc->i >= asc->list->childnum || asc->list->child[ asc->i ]->type != assignsymbol ) {
		Ascorbic_Error( asc, "expected assignsymbol" );
		return NULL;
		}
	
	/* skip assignsymbol */
	asc->i++;
	
	/* end of tokens */
	if( asc->i >= asc->list->childnum ) {
		Ascorbic_Error( asc, "expected expression" );
		return NULL;
		}
	
	/* get expression */
	expr = Parse_Expression( asc, 0 );
	
	/* no expression */
	if( expr == NULL ) return NULL;
	
	/* make node */
	particle = Particle_New( assign, var->place );
	Particle_Add( particle, var );
	Particle_Add( particle, expr );
	
	return particle;
	}

struct Particle *Parse_Block( struct Ascorbic *asc ) {
	struct Particle *particle, *child;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	
	/* particle */
	particle = asc->list->child[ asc->i ];
	
	/* particle is not colonsymbol */
	if( particle->type != colonsymbol ) {
		Ascorbic_Error( asc, "expected colonsymbol" );
		return NULL;
		}
	
	/* particle is good */
	asc->i++;
	
	/* loop over statements */
	while( 1 ) {
		/* skip endlines */
		while( asc->i < asc->list->childnum && asc->list->child[ asc->i ]->type == endline ) asc->i++;
		
		/* no more tokens */
		if( asc->i >= asc->list->childnum ) break;
		
		/* get statement */
		child = Parse_Statement( asc );
		
		/* no statement */
		if( child == NULL ) break;
		
		/* add statement */
		Particle_Add( particle, child );
		}
	
	/* skip endlines */
	while( asc->i < asc->list->childnum && asc->list->child[ asc->i ]->type == endline ) asc->i++;

	/* no semicolonsymbol */
	if( asc->i >= asc->list->childnum || asc->list->child[ asc->i ]->type != semicolonsymbol ) {
		/*Ascorbic_Error( asc, "expected semicolonsymbol" );*/
		return NULL;
		}

	/* skip semicolonsymbol */
	asc->i++;
	
	/* change particle to block type */
	particle->type = statementblock;
	
	return particle;
	}

int Parse_CreateTree( struct Ascorbic *asc ) {
	struct Particle *child;
	int p;
	
	assert( asc );
	assert( asc->list );
	assert( asc->list->childnum > 0 );
	
	/* output tree */
	asc->tree = Particle_New( vitaminc, 0 );
	
	/* process list particles */
	asc->i = 0;

	/* skip leading newlines */
	while( asc->i < asc->list->childnum && asc->list->child[ asc->i ]->type == endline ) asc->i++;

	/* process each sub */
	while( asc->i < asc->list->childnum ) {
		/* process sub */
		child = Parse_Sub( asc );
		if( child == NULL ) return -1;
		
		/* add child */
		Particle_Add( asc->tree, child );

		/* skip endlines */
		while( asc->i < asc->list->childnum && asc->list->child[ asc->i ]->type == endline ) asc->i++;
		}
	
	/* done with list */
	free( asc->list->child );
	free( asc->list );
	asc->list = NULL;
	asc->i = -1;
	
	return 1;
	}

struct Particle *Parse_Definition( struct Ascorbic *asc ) {
	struct Particle *particle, *child;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	
	/* particle */
	particle = asc->list->child[ asc->i ];
	asc->i++;

	/* not definition keyword */
	if( 	particle->type != intkeyword &&
		particle->type != floatkeyword &&
		particle->type != stringkeyword ) {
		Ascorbic_Error( asc, "expected definition" );
		return NULL;
		}
	
	/* identifier does not exist */
	if( asc->i >= asc->list->childnum || asc->list->child[ asc->i ]->type != identifier ) {
		Ascorbic_Error( asc, "expected identifier" );
		return NULL;
		}
	
	/* add identifier */
	Particle_Add( particle, asc->list->child[ asc->i ] );
	asc->i++;
	
	/* assignment symbol does not exist */
	if( asc->i >= asc->list->childnum || asc->list->child[ asc->i ]->type != assignsymbol ) return particle;
	
	/* skip assignsymbol */
	asc->i++;
	
	/* get expression */
	child = Parse_Expression( asc, 0 );
		
	/* no expression */
	if( child == NULL ) return NULL;
	
	/* add expression */
	Particle_Add( particle, child );
	
	return particle;
	}

struct Particle *Parse_Expression( struct Ascorbic *asc, int prevprecedence ) {
	struct Particle *first, *input;
	int precedence, inc = 0;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	
	/* first input */
	first = asc->list->child[ asc->i ];
	
	/* parens */
	if( first->type == leftparensymbol ) {
		first = Parse_Parens( asc );
		if( first == NULL ) return NULL;
		}
	
	/* identifier / subcall */
	else if( first->type == identifier ) {
		first = Parse_Identifier( asc );
		if( first == NULL ) return NULL;
		}
	
	/* negative */
	else if( first->type == subtractsymbol ) {
		/* make node, 0 - expression */
		first = Particle_New( subtract, first->place );
		asc->i++;
		
		/* 0 */
		input = Particle_New( integer, first->place );
		input->data = 0;
		Particle_Add( first, input );
		
		/* expression */
		input = Parse_Expression( asc, 1000 );
		if( input == NULL ) return NULL;
		Particle_Add( first, input );
		}
	
	/* token is committed */
	else inc = 1;

	while( 1 ) {
		/* expression types */
		if( 	first->type == integer ||
			first->type == floatingpoint ||
			first->type == string ||
			first->type == or ||
			first->type == xor ||
			first->type == lsh ||
			first->type == rsh ||
			first->type == gt ||
			first->type == lt ||
			first->type == equal ||
			first->type == subtract ||
			first->type == add ||
			first->type == multiply ||
			first->type == divide ||
			first->type == identifier ) {
			
			/* skip the very first token */
			if( inc ) {
				asc->i++;
				inc = 0;
				}
			
			/* no more source */
			if( asc->i >= asc->list->childnum ) return first;
			
			/* next token */
			input = asc->list->child[ asc->i ];
			
			/* token precedence */
			precedence = Parse_Precedence( input->type );
			
			/* previous token has higher precedence */
			if( prevprecedence > precedence ) return first;
			
			/* token type */
			switch( input->type ) {
				case orsymbol:
					first = Parse_Generic( asc, first, or );
					break;
				case xorsymbol:
					first = Parse_Generic( asc, first, xor );
					break;
				case lshsymbol:
					first = Parse_Generic( asc, first, lsh );
					break;
				case rshsymbol:
					first = Parse_Generic( asc, first, rsh );
					break;
				case gtsymbol:
					first = Parse_Generic( asc, first, gt );
					break;
				case ltsymbol:
					first = Parse_Generic( asc, first, lt );
					break;
				case equalsymbol:
					first = Parse_Generic( asc, first, equal );
					break;
				case subtractsymbol:
					first = Parse_Generic( asc, first, subtract );
					break;
				case addsymbol:
					first = Parse_Generic( asc, first, add );
					break;
				case multiplysymbol:
					first = Parse_Generic( asc, first, multiply );
					break;
				case dividesymbol:
					first = Parse_Generic( asc, first, divide );
					break;
				default:
					return first;
				}
				
			/* expression failed */
			if( first == NULL ) return NULL;
			}
		/* unknown type */
		else {
			Ascorbic_Error( asc, "value expected" );
			return NULL;
			}
		}
	
	return NULL;
	}

struct Particle *Parse_Generic( struct Ascorbic *asc, struct Particle *left, int product ) {
	struct Particle *right, *particle;
	int precedence;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	assert( left );
	assert( left->type > -1 );
	assert( left->place > -1 );
	
	/* precedence of operator */
	precedence = Parse_Precedence( asc->list->child[ asc->i ]->type );
	
	/* skip operator */
	asc->i++;
	
	right = Parse_Expression( asc, precedence );
	
	/* no expression */
	if( right == NULL ) return NULL;
	
	/* make node */
	particle = Particle_New( product, left->place );
	Particle_Add( particle, left );
	Particle_Add( particle, right );
	
	return particle;
	}

struct Particle *Parse_Identifier( struct Ascorbic *asc ) {
	struct Particle *particle, *child;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	
	/* identifier */
	particle = asc->list->child[ asc->i ];
	assert( particle->type == identifier );
	asc->i++;
	
	/* more tokens, possible parameters */
	if( asc->i < asc->list->childnum ) {
		child = Parse_Expression( asc, 0 );
		if( child != NULL ) Particle_Add( particle, child );
		}
	
	return particle;
	}

struct Particle *Parse_Parens( struct Ascorbic *asc ) {
	struct Particle *particle, *child;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	assert( asc->list->child[ asc->i ]->type == leftparensymbol );
	
	/* skip leftparensymbol */
	asc->i++;
	
	/* end of source */
	if( asc->i >= asc->list->childnum ) {
		Ascorbic_Error( asc, "expected expression" );
		return NULL;
		}
	
	/* get expression */
	child = Parse_Expression( asc, 0 );
	
	/* no expression */
	if( child == NULL ) return NULL;
	
	/* no rightparensymbol */
	if( asc->i >= asc->list->childnum || asc->list->child[ asc->i ]->type != rightparensymbol ) {
		Ascorbic_Error( asc, "expected right parenthesis, )" );
		return NULL;
		}
	
	/* skip rightparensymbol */
	asc->i++;
	
	/* return the expression */
	return child;
	}

int Parse_Precedence( int type ) {
	switch( type ) {
		case orsymbol: return 1;
		case xorsymbol: return 2;
		case equalsymbol: return 3;
		case lshsymbol: return 4;
		case rshsymbol: return 5;
		case gtsymbol: return 6;
		case ltsymbol: return 7;
		case subtractsymbol: return 8;
		case addsymbol: return 9;
		case multiplysymbol: return 10;
		case dividesymbol: return 11;
		}
	return 0;
	}

struct Particle *Parse_Ret( struct Ascorbic *asc ) {
	struct Particle *particle, *child;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	
	/* particle */
	particle = asc->list->child[ asc->i ];
	assert( particle->type == returnkeyword );
	asc->i++;
	
	/* end of tokens */
	if( asc->i >= asc->list->childnum ) return particle;
	
	/* get expression */
	child = Parse_Expression( asc, 0 );
	
	/* no expression */
	if( child == NULL ) return particle;
	
	/* add expression */
	Particle_Add( particle, child );
	
	return particle;
	}

struct Particle *Parse_Statement( struct Ascorbic *asc ) {
	struct Particle *particle, *child;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	
	/* particle */
	particle = asc->list->child[ asc->i ];
	
	/* type */
	switch( particle->type ) {
		case intkeyword:
		case floatkeyword:
		case stringkeyword:
			return Parse_Definition( asc );
		case returnkeyword:
			return Parse_Ret( asc );
		case identifier:
			if( asc->i + 1 < asc->list->childnum ) {
				particle = asc->list->child[ asc->i + 1 ];
				if( particle->type == assignsymbol ) return Parse_Assign( asc );
				}
			return Parse_Identifier( asc );
		}

	Ascorbic_Error( asc, "expected statement" );
	return NULL;
	}

struct Particle *Parse_Sub( struct Ascorbic *asc ) {
	struct Particle *particle, *child;
	
	assert( asc );
	assert( asc->list );
	assert( asc->i > -1 );
	assert( asc->i < asc->list->childnum );
	
	/* particle */
	particle = asc->list->child[ asc->i ];
	asc->i++;

	/* not sub keyword */
	if( particle->type != subkeyword ) {
		Ascorbic_Error( asc, "expected sub" );
		return NULL;
		}
	
	/* identifier does not exist */
	if( asc->i >= asc->list->childnum || asc->list->child[ asc->i ]->type != identifier ) {
		Ascorbic_Error( asc, "expected identifier" );
		return NULL;
		}
	
	/* add identifier */
	Particle_Add( particle, asc->list->child[ asc->i ] );
	asc->i++;
	
	/* no more tokens */
	if( asc->i >= asc->list->childnum ) {
		Ascorbic_Error( asc, "expected parameter or block" );
		return NULL;
		}
	
	/* next particle */
	child = asc->list->child[ asc->i ];

	/* is definition keyword */
	if( 	child->type == intkeyword ||
		child->type == floatkeyword ||
		child->type == stringkeyword ) {
		/* get parameter definition */
		child = Parse_Definition( asc );
	
		/* definition not found */
		if( child == NULL ) return NULL;

		/* add definition */
		Particle_Add( particle, child );
		}

	/* no more tokens */
	if( asc->i >= asc->list->childnum ) {
		Ascorbic_Error( asc, "expected block" );
		return NULL;
		}
	
	/* get block */
	child = Parse_Block( asc );
	
	/* no block */
	if( child == NULL ) return NULL;

	/* add block */
	Particle_Add( particle, child );
	
	return particle;
	}
