//
//  EXPDDEModelLoader.m
//  dde
//
//  Created by ashley on 16/10/2007.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "EXPDDEModelLoader.h"
//#import "EXPSymbolTable.h"
#import "EXPModel.h"
#import "EXPDimensionElement.h"
#import "EXPDimensionItemElement.h"
#import "EXPAuxiliaryElement.h"
#import "EXPParameterElement.h"
#import "EXPStateElement.h"
#import "EXPSwitchElement.h"
#import "EXPTableElement.h"
#import "EXPPrintElement.h"
#import "EXPPlotElement.h"
#import "EXPSymbolReference.h"
#import "EXPAssignmentOp.h"
#import "EXPOutputItemOp.h"
#import "EXPTableDefinitionOp.h"
#import "EXPIfThenElseStatement.h"
#import "EXPWhileStatementElement.h"
#import "EXPForLoopStatement.h"
#import "EXPBlockElement.h"
#import "EXPStatementBlockElement.h"
#import "EXPFunctionBlockElement.h"
#import "EXPConstant.h"
#import "EXPFormalArgumentElement.h"
#import "EXPUserFunctionElement.h"
#import "EXPSymbolTable.h"
#import "EXPFunctionElement.h"
#import "EXPParser.h"
#import "EXPError.h"
//#include "interp.h"

#define STEPWISE 0 
#define LINEAR 1
#define SPLINE 2

@implementation EXPDDEModelLoader

- (void) declareBuiltinVariables:(EXPBlockElement *)model
{
	[self declareBuiltinParameter:@"tstart" forModel:model];
	[self declareBuiltinParameter:@"tstop" forModel:model];
	[self declareBuiltinAuxiliary:@"t" forModel:model];
}

- (BOOL) setAssignmentOpRHS:(EXPAssignmentOp *)assignment forModel:(EXPBlockElement *)model destination:(id)dest error:(EXPError *)error
{
	BOOL success = YES;
	EXPParser *parser = [self parser];
	
	EXPSymbolReference *left = [assignment left];
	NSArray *indices = [left indices];
	EXPExpression *right;
	if ([parser testSymbol:@"{"]) {
		do {
			EXPExpression *exp = [self expression:model indices:nil isLeft:NO error:error];
			if (exp==nil) {
				success = NO;
				return success;
			}
			[assignment addRight:exp];
			
		} while ([parser testSymbol:@","]);
		[parser checkSymbol:@"}"];
	} else {
		right = [self expression:model indices:indices isLeft:NO error:error];
		if (right==nil) {
			return NO;
		}
		NSMutableArray *rightHandSide = [[NSMutableArray alloc] initWithObjects:right, nil];
		[assignment setRight:rightHandSide];
		[rightHandSide release];
	}
	EXPElement *element = [left element];
	NSString *leftElementType = [element elementType];
//	NSLog(@"\nsetAssignmentOpRHS: model = %08x dest =%08x.", (unsigned int)model, (unsigned int)dest);
	if ((unsigned int)model!=(unsigned int)dest) {
		[error addError:@"Oops! model!=dest!!" atLine:-999999];
	}

//	Farm out to EXPAssignmentOp
	EXPExpressionType leftType = [element expressionType];
	NSMutableArray *rhs = [assignment right];
	int i;
	for(i=0; i<[rhs count]; i++) {
		EXPExpression *exp = [rhs objectAtIndex:i];
		EXPExpressionType rightType = [exp expressionType];
		if ((leftType==integerType) && (rightType==doubleType)) {
			NSString *message = [[NSString alloc] initWithFormat:@"Cannot assign double value to integer variable \"%@\".", [[left element] name]];
			[error addError:message atLine:[parser lineNumber]];
			[message release];
		} else if ((leftType==doubleType) && (rightType==integerType)) {
			if ([[exp elementType] isEqualToString:@"constant"]) {
				EXPConstant *constant = (EXPConstant *)exp;
				[constant setExpressionType:doubleType];
			} else {
				EXPUnaryOp *doubleOp = [[EXPUnaryOp alloc] initOp:@"float" withLeft:exp];
				[doubleOp setExpressionType:doubleType];
				[rhs replaceObjectAtIndex:i withObject:doubleOp];
				[doubleOp release];
			}
		}
	}

	if (([left primes]>0) || [leftElementType isEqualToString:@"auxiliary"]) {
		[dest addStatement:assignment error:error];
	} else {
		[dest addInitialiser:assignment error:error];
	}

	return success;
}

- (BOOL) setSymbolReference:(EXPSymbolReference *)element forModel:(EXPBlockElement *)model forAssignment:(id)assignment 
		isLeft:(BOOL)left error:(EXPError *)error
{
	BOOL success = YES;
	EXPSymbolTable *symbolTable = [model symbolTable];
	EXPParser *parser = [self parser];
	
/*	BOOL haveSmooth1 = NO;
	BOOL haveSmooth3 = NO;
	BOOL haveSmooth5 = NO;
	if ((haveSmooth1 = [parser testSymbol:@"smooth1"]) || (haveSmooth3 = [parser testSymbol:@"smooth3"]) || (haveSmooth5 = [parser testSymbol:@"smooth5"])) {
		if (![parser checkSymbol:@"("]) {
			return NO;
		}
		EXPExpression *smooth = [self expression:model indices:nil isLeft:NO error:error];
		if (smooth==nil) {
			return NO;
		}

		if (![parser checkSymbol:@","]) {
			return NO;
		}

		EXPExpression *timeConstant = [self expression:model indices:nil isLeft:NO error:error];
		if (timeConstant==nil) {
			return NO;
		}

		if (![parser checkSymbol:@")"]) {
			return NO;
		}

		int smoothOrder;
		if (haveSmooth1) {
			smoothOrder = 1;
		} else if (haveSmooth3) {
			smoothOrder = 3;
		} else {
			smoothOrder = 5;
		}
		
		id lastState = smooth;
		int i;
		for(i=0; i<smoothOrder; i++) {
			EXPStateElement *state = [[EXPStateElement alloc] init];
			NSString *stateName = [[NSString alloc] initWithFormat:@"__SMOOTHSTATE_%d", nextSmoothStateNumber++];
			[state setName:stateName];
			[stateName release];
			[model addVariable:state];
			[state release];

//	Set up initialiser
			EXPSymbolReference *left = [[EXPSymbolReference alloc] init];
			[left setElement:state];
			[left setIndices:[state dimensions]];
			[left setDimensions:[state dimensions]];
			
			EXPSymbolReference *right = [[EXPSymbolReference alloc] init];
			[right setElement:lastState];
			[right setIndices:[lastState dimensions]];
			[right setDimensions:[lastState dimensions]];
			
			NSArray *rhs = [[NSArray alloc] initWithObjects:right, nil];			
			EXPAssignmentOp *assignment = [[EXPAssignmentOp alloc] initWithLeft:left right:rhs primes:0];
			[assignment storeIndices];
			[rhs release];

			[model addInitialiser:assignment];
			[assignment release];
			[left release];
			[right release];

//	Euchhh!
			lastState = state;
		}

		return success;
	} */
	
	NSString *symbolName = [parser symbol];
	id sym = [symbolTable symbolForName:symbolName];
	if (sym==nil) {
		[error addError:[NSString stringWithFormat:@"Undeclared Symbol %@", [parser symbol]] atLine:[parser lineNumber]];
	}
	
	BOOL isSimple = ![[sym elementType] isEqualToString:@"function"] && ![[sym elementType] isEqualToString:@"table"];
	[element setSimple:isSimple];	
	[element setElement:sym];
	[element setExpressionType:[sym expressionType]];
	[parser readSymbol];
	
	NSMutableArray *list = nil;
	NSMutableArray *indices = [[NSMutableArray alloc] init];

	if ([parser testSymbol:@"["]) {	
		list = [[NSMutableArray alloc] init];
		NSArray *symbolDimensions = nil;
		int numSymbolDimensions = 0;
		if ([sym respondsToSelector:@selector(dimensions)]) {
			symbolDimensions = [sym dimensions];
			numSymbolDimensions = [symbolDimensions count];
		
		}
		
		int dimensionNumber = 0;
		BOOL tooManyDimensions = NO;
		do {
			EXPExpression *exp = [self expression:model indices:indices isLeft:left error:error];
			if (exp==nil) {
				break;
			}
			
			if (dimensionNumber>numSymbolDimensions) {
				if (!tooManyDimensions) {
					[error addError:[NSString stringWithFormat:@"Too many dimensions..."] atLine:[parser lineNumber]];
				}
				tooManyDimensions = YES;
				continue;
			}
			
//	Hmmmm......
			EXPDimensionElement *dimension = nil;
			if (dimensionNumber<[symbolDimensions count]) {
				dimension = [symbolDimensions objectAtIndex:dimensionNumber];
			} else {
//				 success = NO;
				 [error addError:@"Not enough dimensions!" atLine:[parser lineNumber]];
				 break;
			}

			if (([exp expressionType]==dimensionType) && !([indices containsObject:exp])) {
//				[indices addObject:exp];
				[indices addObject:dimension];
				[element setSimple:NO];
			}
			
			if ([exp expressionType]==doubleType) {
				[error addError:[NSString stringWithFormat:@"Invalid dimension type..."] atLine:[parser lineNumber]];
				continue;
			}
			
			if ([dimension isOrderedDimension]) {
				if (!(([exp expressionType]==integerType) || ([exp expressionType]==dimensionType))) {
					[error addError:[NSString stringWithFormat:@"Invalid dimension type..."] atLine:[parser lineNumber]];
					continue;
				}
			} else {
				EXPElement *indexElement = [(EXPSymbolReference *)exp element];
				NSString *elementType = [indexElement elementType];
				if (![elementType isEqualToString:@"dimension"] && ![elementType isEqualToString:@"dimensionItem"] &&
					![elementType isEqualToString:@"index"] && !([elementType isEqualToString:@"auxiliary"] && [indexElement expressionType]==integerType)) {
						[error addError:[NSString stringWithFormat:@"Invalid dimension type..."] atLine:[parser lineNumber]];
					continue;
				} 
			}
			
//			[list addObject:dimension];
			[list addObject:exp];

			dimensionNumber++;
			
		} while ([parser testSymbol:@","]);
		
		[parser checkSymbol:@"]"];

	} else {
		if ([sym respondsToSelector:@selector(dimensions)]) {
			list = [[sym dimensions] retain];
		} else {
			list = [[NSMutableArray alloc] init];
		}
	}
	
	[element setIndices:indices];
	[element setDimensions:list];
	[list release];

	int nPrimes = 0;
	while ([parser testSymbol:@"'"]) {
		nPrimes++;
	}
	[element setPrimes:nPrimes];

	NSMutableArray *argList = [[NSMutableArray alloc] init];
	if ([parser testSymbol:@"("]) {
		[element setSimple:NO];
        do {
			EXPExpression *arg = [self expression:model indices:indices isLeft:left error:error];
			[argList addObject:arg];
		} while ([parser testSymbol:@","]); 
            
		[parser checkSymbol:@")"];
	}	
	
	[element setArguments:argList];
	if (assignment!=nil) {
		NSArray *indexList = [[NSArray alloc] initWithArray:indices];
		[assignment setIndices:indexList];
		[indexList release];
	}
	
	[argList release];
	[indices release];
	
	if ([[sym elementType] isEqualToString:@"function"]) {
//		printf("Got a function!\n");
	}
		
	return success;
}

- (EXPSymbolReference *) leftValue:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = YES;
	EXPSymbolTable *symbolTable = [model symbolTable];
	EXPParser *parser = [self parser];
	
	if (![parser isIdentifier]) {
		[parser readSymbol];
		return nil;
	}
	
	EXPSymbolReference *p = [[EXPSymbolReference alloc] init];
	id sym = [symbolTable symbolForName:[parser symbol]];

//	Test if sym exists --- what if it's an auxiliary?
	if (sym==nil) {
		[error addError:[NSString stringWithFormat:@"Undeclared Symbol %@", [parser symbol]] atLine:[parser lineNumber]];
	}
	
	[p setElement:sym];
	[p setExpressionType:[sym expressionType]];
	
	success = [self setSymbolReference:p forModel:model forAssignment:nil isLeft:YES error:error];
	if ([[p arguments] count]>0) {
		[error addError:[NSString stringWithFormat:@"Invalid Lefthandside %@", [sym name]] atLine:[parser lineNumber]];
	}

	return [p autorelease];
}

- (id) assignment:(EXPBlockElement *)model /*indices:(NSArray *)indices isLeft:(BOOL)isLeft*/ error:(EXPError *)error
{
	EXPParser *parser = [self parser];
	id p;
	
	EXPExpression *lhs = [self expression:model indices:nil isLeft:YES error:error];
	if (lhs==nil) {
		return nil;
	}
	
	if ([parser testSymbol:@"="]) {
		EXPAssignmentOp *assignment = [[[EXPAssignmentOp alloc] initWithLeft:lhs right:nil/* primes:nPrimes*/] autorelease];
		[assignment storeIndices];
			
		[self setAssignmentOpRHS:assignment forModel:model destination:model /*withSelector:sel*/ error:error];
		p = assignment;
	} else {
		[model addStatement:lhs error:error];
		p = lhs;
	}
	
	return p;
}

- (BOOL) processOption:(EXPBlockElement *)model error:(EXPError *)error
{
	EXPParser *parser = [self parser];

	NSString *optionName = [[NSString alloc] initWithString:[parser symbol]];
	[parser readSymbol];

	NSString *optionValue;
	if ([parser testSymbol:@"="]) {
		BOOL negative = [parser testSymbol:@"-"];
		optionValue = [[NSString alloc] initWithString:[parser symbol]];
		if (!([parser isLiteral] || [parser isStringLiteral])) {
			[optionName release];
			[optionValue release];
			return NO;
		}

		[parser readSymbol];
		
		if (negative) {
			NSString *str = [[NSString alloc] initWithFormat:@"-%@", optionValue];
			[optionValue release];
			optionValue = str;
		}
	} else {
		optionValue = @"";
	}

	BOOL success = YES;
	
	[model setOption:optionValue forName:optionName];
	
	[optionName release];
	[optionValue release];
	
	return success;
}

- (id) getElementForModel:(EXPBlockElement *)model error:(EXPError *)error
{
	EXPSymbolTable *symbolTable = [model symbolTable];
	EXPParser *parser = [self parser];

	EXPNamedElement *element = nil;
	if ([parser isIdentifier]) {
		NSString *identifier = [[NSString alloc] initWithString:[parser symbol]];
		[parser readSymbol];
		element = [symbolTable symbolForName:identifier];
		[identifier release];

	} else {
		[error addError:@"Identifier expected..." atLine:[parser lineNumber]];
	}

	return element;
}

- (BOOL) setElement:(EXPElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	EXPParser *parser = [self parser];
	int lineNumber = [parser lineNumber];
	[element setInitLineNumber:lineNumber];
	
	return YES;
}

- (BOOL) setNamedElement:(EXPNamedElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	EXPSymbolTable *symbolTable = [model symbolTable];
	EXPParser *parser = [self parser];

	BOOL success = [self setElement:element forModel:model error:error];

	if ([parser isIdentifier]) {
		NSString *identifier = [[NSString alloc] initWithString:[parser symbol]];
		[parser readSymbol];
		[element setName:identifier];
		[identifier release];
	} else {
		[error addError:@"Identifier expected..." atLine:[parser lineNumber]];
		return NO;
	}
			
	success = [symbolTable declareSymbol:element];
	if (!success) {
		[error addError:[NSString stringWithFormat:@"Redeclared symbol %@...", [element name]] atLine:[parser lineNumber]];
	}

	return success;
}

- (BOOL) setDimensionItemElement:(EXPDimensionItemElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = [self setNamedElement:element forModel:model error:error];
	
	return success;
}

- (BOOL) setDimensionElement:(EXPDimensionElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	EXPParser *parser = [self parser];

	BOOL success  = [self setNamedElement:element forModel:model error:error];

	[parser checkSymbol:@"="];
	
	if ([parser isLiteral]) {
		[element setOrderedDimension:YES];
		[element setLowerBound:[[parser symbol] intValue]];
		[parser readSymbol];
		[parser checkSymbol:@":"];
		[element setUpperBound:[[parser symbol] intValue]];
		[parser readSymbol];

	} else if ([parser testSymbol:@"{"]) {
		[element setOrderedDimension:NO];
		do {
			EXPDimensionItemElement *item = [[EXPDimensionItemElement alloc] init];
			success  = [self setDimensionItemElement:item forModel:model error:error];
			[item setDimension:element];
			[element addItem:item];
			[item release];
		} while ([parser testSymbol:@","]);
		[parser checkSymbol:@"}"];
		
		[element setLowerBound:0];
		[element setUpperBound:[[element dimensionList] count]];

	} else {
	}
	
	return success;
}


- (BOOL) setDimensionedElement:(EXPDimensionedElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = [self setNamedElement:element forModel:model error:error];

	EXPParser *parser = [self parser];

	if ([parser testSymbol:@"["]) {
		do {
			EXPDimensionElement *dimension = [self getElementForModel:model error:error];
			if ([[dimension elementType] isEqualToString:@"dimension"]) {
				[element addDimension:dimension];
			} else {
				[error addError:[NSString stringWithFormat:@"Dimension expected %@...", [dimension name]] atLine:[parser lineNumber]];
			}
			
		} while ([parser testSymbol:@","]);
		
		[parser checkSymbol:@"]"];

	}
	
	return success;
}

- (BOOL) setDataElement:(EXPDataElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success  = [self setDimensionedElement:element forModel:model error:error];

	EXPParser *parser = [self parser];

	if ([parser testSymbol:@"="]) {
		EXPSymbolReference *left = [[EXPSymbolReference alloc] init];
		[left setElement:element];
		[left setIndices:[element dimensions]];
		[left setDimensions:[element dimensions]];
		NSMutableArray *rhs = [[NSMutableArray alloc] init];
		EXPAssignmentOp *assignment = [[EXPAssignmentOp alloc] initWithLeft:left right:rhs/* primes:0*/];
		[assignment storeIndices];
		[left setPrimes:0];
		[rhs release];
			
		success = [self setAssignmentOpRHS:assignment forModel:model destination:model /*withSelector:@selector(addStatement:)*/ error:error];
		
		[assignment release];
		[left release];
		
		if (!success) {
			return NO;
		}
	}

	if ([parser testSymbol:@"label"]) {
		if (![parser checkSymbol:@"="]) {
			success = NO;
		}

//		[parser readSymbol];
		if (![parser isStringLiteral]) {
			return NO;
		}

		NSString *label = [[NSString alloc] initWithString:[parser symbol]];
		[parser readSymbol];
		[element setLabel:label];
		[label release];

	}
	
//	[element setExpressionType:doubleType];

	return success;
}

- (BOOL) setAuxiliaryElement:(EXPAuxiliaryElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	[element setExpressionType:doubleType];
	BOOL success = [self setDataElement:element forModel:model error:error];
	
/*	EXPParser *parser = [self parser];
	if ([parser testSymbol:@"static"]) {
		[element setIsStatic:YES];
	} */
	
	return success;
}

- (BOOL) setIndexElement:(EXPAuxiliaryElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	EXPParser *parser = [self parser];
	if ([parser testSymbol:@"into"]) {
		EXPSymbolTable *symbolTable = [model symbolTable];
		NSString *boundDimensionName = [[NSString alloc] initWithString:[parser symbol]];
		[parser readSymbol];
		id symbol = [symbolTable symbolForName:boundDimensionName];
		if (symbol==nil) {
			NSString *message = [[NSString alloc] initWithFormat:@"Cannot bind index variable to undeclared dimension %@", boundDimensionName];
			[error addError:message atLine:[parser lineNumber]];
			[message release];
		} else {
			if ([[symbol elementType] isEqualToString:@"dimension"]) {
				[element setDimension:symbol];
			} else {
				NSString *message = [[NSString alloc] initWithFormat:@"Cannot bind index variable to non-dimension variable %@", boundDimensionName];
				[error addError:message atLine:[parser lineNumber]];
				[message release];
			}
		}
	}

	BOOL success = [self setDataElement:element forModel:model error:error];
	[element setExpressionType:integerType];
	
	return success;
}

- (BOOL) setStateElement:(EXPStateElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	[element setExpressionType:doubleType];
	BOOL success = [self setDataElement:element forModel:model error:error];
	
	EXPParser *parser = [self parser];

	if ([parser testSymbol:@"delay"]) {
		[element setIsDelayVariable:YES];
	}
	
	if ([parser testSymbol:@"scale"]) {
		if ([parser checkSymbol:@"="]) {
			if ([parser isLiteral]) {
				double scale = [[parser symbol] doubleValue];
				[parser readSymbol];
				[element setScale:scale];
			}
		}
	}
			
	return success;
}

- (BOOL) setParameterElement:(EXPParameterElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	[element setExpressionType:doubleType];
	BOOL success = [self setDataElement:element forModel:model error:error];
	
	return success;
}

- (BOOL) setTableElement:(EXPTableElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
//	[element setExpressionType:doubleType];
	BOOL success = [self setNamedElement:element forModel:model error:error];
	
	EXPParser *parser = [self parser];	
	if (![parser checkSymbol:@"="]) {
		return NO;
	}
	
	BOOL isRegular = [parser testSymbol:@"regular"];
	[element setIsRegular:isRegular];
			
	NSString *tableType = [[NSString alloc] initWithString:[parser symbol]];
	[parser readSymbol];
	
	if (![parser checkSymbol:@"("]) {
		[tableType release];
		return NO;
	}
	
	int nPoints = 0;
	if (isRegular) {
		for(;;) {
			EXPSymbolReference *exp = [self expression:model indices:nil isLeft:NO error:error];
			[element addYPoint:exp];

			nPoints++;
			if (![parser testSymbol:@","]) {
				break;
			}
		}
	} else {
		for(;;) {
			if (![parser checkSymbol:@"{"]) {
				success = NO;
				break;
			}
		
			EXPSymbolReference *exp = [self expression:model indices:nil isLeft:NO error:error];
			[element addXPoint:exp];

			if (![parser checkSymbol:@","]) {
				success = NO;
				break;
			}
		
			exp = [self expression:model indices:nil isLeft:NO error:error];
			[element addYPoint:exp];

			nPoints++;

			if (![parser checkSymbol:@"}"]) {
				success = NO;
				break;
			}
		
			if (![parser testSymbol:@","]) {
				break;
			}
		}
	}
			
	if (![parser checkSymbol:@")"]) {
		[tableType release];
		return NO;
	}
	
	if ([parser testSymbol:@"xmin"]) {
		if (![parser checkSymbol:@"="]) {
			return NO;
		}
		EXPSymbolReference *exp = [self expression:model indices:nil isLeft:NO error:error];
		[element setXMin:exp];
	}
			
	if ([parser testSymbol:@"xmax"]) {
		if (![parser checkSymbol:@"="]) {
			return NO;
		}
		EXPSymbolReference *exp = [self expression:model indices:nil isLeft:NO error:error];
		[element setXMax:exp];
	}
			
	if ([tableType isEqualToString:@"linear"]) {
		[element setInterpType:LINEAR];
/*	} else if ([tableType isEqualToString:@"polynomial"]) {
		[element setInterpType:POLYNOMIAL]; */
	} else if ([tableType isEqualToString:@"cspline"] || [tableType isEqualToString:@"spline"]) {
		[element setInterpType:SPLINE];
	} else if ([tableType isEqualToString:@"piecewise"]) {
		[element setInterpType:STEPWISE];
	} else {
		[error addError:@"Invalid interpolator name for table function" atLine:[parser lineNumber]];
	}
	
	EXPSymbolReference *reference = [[EXPSymbolReference alloc] init]; 
	[reference setElement:element];
	EXPTableDefinitionOp *definition = [[EXPTableDefinitionOp alloc] initWithLeft:reference right:nil/* primes:0*/];
	[reference release];
	[model addInitialiser:definition error:error];
	[model addTable:element error:error];
	[definition release];

	[tableType release];
	
	return success;
}

- (BOOL) setSwitchElement:(EXPSwitchElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = [self setElement:element forModel:model error:error];
	EXPParser *parser = [self parser];
	EXPSymbolTable *symbolTable = [model symbolTable];
	BOOL local = [element isLocal];
	
	EXPExpression *condition = [self expression:model indices:nil isLeft:NO error:error];
	[element setHeader:condition];

	if (![parser testSymbol:@"do"]) {
		return NO;
	}

	EXPStatementBlockElement *block = [[EXPStatementBlockElement alloc] initWithEnclosingBlock:model];
	while (![parser testSymbol:@"end"] && success) {
		success = [self parseStatement:block symbolTable:symbolTable destination:block isLocal:local error:error];
	} 
	[element setBlock:block];
	[block release];
	
	return success;
}

- (BOOL) setFormalArgumentElement:(EXPFormalArgumentElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	EXPParser *parser = [self parser];
	BOOL functionArgument = [parser testSymbol:@"function"];
	BOOL success = [self setNamedElement:/*(EXPAuxiliaryElement *)*/element forModel:model error:error];
	[element setFunctionArgument:functionArgument];
	
	return success;
}

- (BOOL) setUserFunctionElement:(EXPUserFunctionElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = [self setNamedElement:element forModel:model error:error];
	if (!success) {
		return NO;
	}
	
	EXPParser *parser = [self parser];
//	EXPSymbolTable *symbolTable = [model symbolTable];
//	EXPSymbolTable *symbolTable = [[EXPSymbolTable alloc] init];
	EXPFunctionBlockElement *block = [[EXPFunctionBlockElement alloc] initWithEnclosingBlock:model];
	EXPSymbolTable *symbolTable = [block symbolTable];
	[block setModel:model];
//	[block setSymbolTable:symbolTable];
	
	int nFormalArgs = 0;
	if ([parser testSymbol:@"("]) {
		if (![parser testSymbol:@")"]) {
			do {
				EXPFormalArgumentElement *arg = [[EXPFormalArgumentElement alloc] init];
				success = [self setFormalArgumentElement:arg forModel:block error:error];
				[element  addFormalArgument:arg error:error];
				[arg release];
				
				if (!success) {
					break;
				}
				
				nFormalArgs++;
			} while ([parser testSymbol:@","]);
			success = [parser checkSymbol:@")"] && success;
		}
	}
	
	while (![parser testSymbol:@"end"]) {
		success = [self parseStatement:block symbolTable:symbolTable destination:block isLocal:YES error:error];
			
	} 
	[element setBlock:block];
	[block release];
//	[symbolTable release];

	return YES;
}

- (BOOL) setIfThenElseElement:(EXPIfThenElseStatement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = [self setElement:element forModel:model error:error];
	EXPParser *parser = [self parser];
	EXPSymbolTable *symbolTable = [model symbolTable];
	BOOL local = [element isLocal];
	
	EXPExpression *condition = [self expression:model indices:nil isLeft:NO error:error];
	[element setHeader:condition];

	if (![parser testSymbol:@"then"]) {
		return NO;
	}

	BOOL gotEnd = NO;
	BOOL gotElse = NO;
	BOOL gotElseIf = NO;
	EXPStatementBlockElement *block = [[EXPStatementBlockElement alloc] initWithEnclosingBlock:model];
	while (!((gotEnd = [parser testSymbol:@"end"]) || (gotElse = [parser testSymbol:@"else"]) || (gotElseIf = [parser testSymbol:@"elseif"]))) {
		success = [self parseStatement:block symbolTable:symbolTable destination:block isLocal:local error:error];
	} 
	[element setBlock:block];
	[block release];
	
	while (gotElseIf) {
		while (!(gotEnd = [parser testSymbol:@"end"]) || (gotElse = [parser testSymbol:@"else"]) || (gotElseIf = [parser testSymbol:@"elseif"])) {
			EXPStatementBlockElement *block = [[EXPStatementBlockElement alloc] init];
			[block setModel:model];
			success = [self parseStatement:block symbolTable:symbolTable destination:element isLocal:local error:error];
			[block release];
				
		} 
	}
	
	if (gotElse) {
		EXPStatementBlockElement *block = [[EXPStatementBlockElement alloc] initWithEnclosingBlock:model];
		[block setModel:model];
		[block setSymbolTable:symbolTable];
		while (!(gotEnd = [parser testSymbol:@"end"])) {
			success = [self parseStatement:block symbolTable:symbolTable destination:block isLocal:local error:error];
			
		} 
		[element setElsePart:block];
		[block release];
	}
	
	return success;
}

- (BOOL) setWhileElement:(EXPWhileStatementElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = [self setElement:element forModel:model error:error];
	if (!success) {
		return NO;
	}
	EXPParser *parser = [self parser];
	EXPSymbolTable *symbolTable = [model symbolTable];
	BOOL local = [element isLocal];
	
	EXPExpression *condition = [self expression:model indices:nil isLeft:NO error:error];
	[element setHeader:condition];

	if (![parser checkSymbol:@"do"]) {
		return NO;
	}

	EXPStatementBlockElement *block = [[EXPStatementBlockElement alloc] initWithEnclosingBlock:model];
	while (![parser testSymbol:@"end"]) {
		success = [self parseStatement:block symbolTable:symbolTable destination:block isLocal:local error:error];
	} 
	[element setBlock:block];
	[block release];

	return YES;
}

- (BOOL) setForElement:(EXPForLoopStatement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success;
	EXPParser *parser = [self parser];
	EXPSymbolTable *symbolTable = [model symbolTable];
	BOOL local = [element isLocal];

	EXPExpression *exp = [self expression:model indices:nil isLeft:YES error:error];
	[element setHeader:exp];
	if (![[exp elementType] isEqualToString:@"auxiliary"] || ([exp expressionType]!=integerType)) {
		[error addError:@"Variable in for-loop must be an index"  atLine:[parser lineNumber]];
	}
	if ([[(EXPAuxiliaryElement *)exp indices] count]>0) {
		[error addError:@"Variable in for-loop may not have free dimensions"  atLine:[parser lineNumber]];
	}

	if ([parser testSymbol:@"from"] || [parser testSymbol:@"="]) {
		EXPExpression *fromExp = [self expression:model indices:nil isLeft:NO error:error];
		[element setFromExp:fromExp];
		if (![parser checkSymbol:@"to"]) {
			return NO;
		}
		EXPExpression *toExp = [self expression:model indices:nil isLeft:NO error:error];
		[element setToExp:toExp];
	} else {
		success = [parser checkSymbol:@"in"];
		if (!success) {
			return NO;
		}
		
		EXPExpression *inExp = [self expression:model indices:nil isLeft:NO error:error];
		[element setInDimension:inExp];
		if (![[inExp elementType] isEqualToString:@"dimension"]) {
			[error addError:@"Variable in for-loop must be an dimension"  atLine:[parser lineNumber]];
		}
	}

	if (![parser checkSymbol:@"do"]) {
		return NO;
	}

	EXPStatementBlockElement *block = [[EXPStatementBlockElement alloc] initWithEnclosingBlock:model];
	while (![parser testSymbol:@"end"]) {
		success = [self parseStatement:block symbolTable:symbolTable destination:block isLocal:local error:error];
	} 
	[element setBlock:block];
	[block release];

	return YES;
}

- (BOOL) setOutputElement:(EXPOutputElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = [self setElement:element forModel:model error:error];
	EXPParser *parser = [self parser];

	if ([parser testSymbol:@"to"]) {
		if (![parser isStringLiteral]) {
			success = NO;
			return success;
		}

		NSString *filename = [[NSString alloc] initWithString:[parser symbol]];
		[parser readSymbol];
		[element setOutputFilename:filename];
		[filename release];
	} else {
		[element setOutputFilename:nil];
	}
	
	if ([parser testSymbol:@"as"]) {
		if (![parser isStringLiteral]) {
			success = NO;
			return success;
		}

		NSString *filetype = [[NSString alloc] initWithString:[parser symbol]];
		[parser readSymbol];
		[element setFiletype:filetype];
		[filetype release];
	}

	return success;
}

- (void) getOutputItemIndices:(id)indices forExpression:(id)exp
{
	EXPExpression *p = exp;
	NSString *elementType = [p elementType];
	NSArray *elementIndices = nil;
	if ([elementType isEqualToString:@"auxiliary"] || [elementType isEqualToString:@"variable"] || [elementType isEqualToString:@"parameter"]) {
		elementIndices = [(EXPDataElement *)p indices];
	} else if ([elementType isEqualToString:@"unaryop"]) {
		EXPExpression *left = [(EXPUnaryOp *)p left];
		[self getOutputItemIndices:indices forExpression:left];
	} else if ([elementType isEqualToString:@"binaryop"]) {
		EXPExpression *left = [(EXPBinOp *)p left];
		[self getOutputItemIndices:indices forExpression:left];
		EXPExpression *right = [(EXPBinOp *)p right];
		[self getOutputItemIndices:indices forExpression:right];
	}
	
	int i;
	for(i=0; i<[elementIndices count]; i++) {
		EXPElement *element = [elementIndices objectAtIndex:i];
		if (![indices containsObject:element]) {
			[indices addObject:element];
		}
	}
}

- (BOOL) setOutputItem:(EXPOutputItemOp *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = YES;
	
	EXPExpression *exp = [self expression:model indices:nil isLeft:YES error:error];
	[element setLeft:exp];
//	[element storeIndices];
	NSMutableArray *indices = [NSMutableArray array];
	[self getOutputItemIndices:indices forExpression:exp];
	[element setIndices:indices];
	
	return success;
}

- (BOOL) setPlotElement:(EXPPlotElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = YES;
	EXPParser *parser = [self parser];

	success = [self setOutputElement:element forModel:model error:error];
	
	[element setGrid:NO];
	[element setPlotTitle:@""];
	while (YES) {
		if ([parser testSymbol:@"grid"]) {
			[element setGrid:YES];
		} else if ([parser testSymbol:@"legend"]) {
			[element setLegend:YES];
		} else if ([parser testSymbol:@"title"]) {
			(void)[parser testSymbol:@"="];
			if (![parser isStringLiteral]) {
				success = NO;
				return success;
			}

			NSString *plotTitle = [[NSString alloc] initWithString:[parser symbol]];
			[parser readSymbol];
			[element setPlotTitle:plotTitle];
			[plotTitle release];
		} else if ([parser testSymbol:@"xlabel"]) {
			(void)[parser testSymbol:@"="];
			if (![parser isStringLiteral]) {
				success = NO;
				return success;
			}

			NSString *xlabel = [[NSString alloc] initWithString:[parser symbol]];
			[parser readSymbol];
			[element setXlabel:xlabel];
			[xlabel release];
		} else if ([parser testSymbol:@"ylabel"]) {
			(void)[parser testSymbol:@"="];
			if (![parser isStringLiteral]) {
				success = NO;
				return success;
			}

			NSString *ylabel = [[NSString alloc] initWithString:[parser symbol]];
			[parser readSymbol];
			[element setYlabel:ylabel];
			[ylabel release];
		} else if ([parser testSymbol:@"style"]) {
			(void)[parser testSymbol:@"="];
			if (![parser isStringLiteral]) {
				success = NO;
				return success;
			}

			NSString *style = [[NSString alloc] initWithString:[parser symbol]];
			[parser readSymbol];
			[element setStyle:style];
			[style release];
		} else {
			break;
		}
	}
	
	do {
		EXPOutputItemOp *exp = [[EXPOutputItemOp alloc] init];
		[exp setPrinter:element];
		success = [self setOutputItem:exp forModel:model error:error];
		[element addItem:exp];
		
		NSString *title;
		if ([parser testSymbol:@"title"]) {
			(void)[parser testSymbol:@"="];
			title = [[NSString alloc] initWithString:[parser symbol]];
			[parser readSymbol];
//			[element addLineTitle:title];
		} else {
			title = @"";
//			[element addLineTitle:@""];
		}
		int n = 1;
		NSArray *indices = [exp indices];
		int j;
		for(j=0; j<[indices count]; j++) {
			n *= [[indices objectAtIndex:j] nElements];
		}
		int i;
		for(i=0; i<n;i++) {
			[element addLineTitle:title];
		}
		[title release];
		[exp release];
	} while ([parser testSymbol:@","]);
	
	return success;
}

- (BOOL) setPrintElement:(EXPPrintElement *)element forModel:(EXPBlockElement *)model error:(EXPError *)error
{
	BOOL success = YES;
	EXPParser *parser = [self parser];
	
	success = [self setOutputElement:element forModel:model error:error];
	if (!success) {
		return success;
	}
	
	BOOL haveHeader, haveFooter, haveLeader, haveTrailer, haveSeparator;
	
	while ((haveHeader=[parser testSymbol:@"header"]) || (haveFooter=[parser testSymbol:@"footer"]) || (haveLeader=[parser testSymbol:@"leader"]) ||
		   (haveTrailer=[parser testSymbol:@"trailer"]) || (haveSeparator=[parser testSymbol:@"separator"])) {
		if (haveHeader) {
			if ([parser checkSymbol:@"="]) {
				NSString *header = [[NSString alloc] initWithString:[parser symbol]];
				[parser readSymbol];
				[element setHeader:header];
				[header release];
			}
		} else if (haveFooter) {
			if ([parser checkSymbol:@"="]) {
				NSString *footer = [[NSString alloc] initWithString:[parser symbol]];
				[parser readSymbol];
				[element setFooter:footer];
				[footer release];
			}
		} else if (haveLeader) {
			if ([parser checkSymbol:@"="]) {
				NSString *leader = [[NSString alloc] initWithString:[parser symbol]];
				[parser readSymbol];
				[element setLeader:leader];
				[leader release];
			}
		} else if (haveTrailer) {
			if ([parser checkSymbol:@"="]) {
				NSString *trailer = [[NSString alloc] initWithString:[parser symbol]];
				[parser readSymbol];
				[element setTrailer:trailer];
				[trailer release];
			}
		} else if (haveSeparator) {
			if ([parser checkSymbol:@"="]) {
				NSString *separator = [[NSString alloc] initWithString:[parser symbol]];
				[parser readSymbol];
				[element setSeparator:separator];
				[separator release];
			}
		}
	}
	
	do {
		EXPOutputItemOp *exp = [[EXPOutputItemOp alloc] init];
		[exp setPrinter:element];
		success = [self setOutputItem:exp forModel:model error:error];
		[element addItem:exp];
		[exp release];
	} while ([parser testSymbol:@","]);
	
	return success;
}

- (BOOL) parseStatement:(EXPBlockElement *)model symbolTable:(EXPSymbolTable *)parentSymbolTable destination:(id)dest isLocal:(BOOL)isLocal error:(EXPError *)error
{
	BOOL success = YES;
	EXPSymbolTable *symbolTable = nil;
	EXPParser *parser = [self parser];
	
	if ([parser testSymbol:@"dim"] || [parser testSymbol:@"dimension"]) {
		do {
			EXPDimensionElement *element = [[EXPDimensionElement alloc] init];
			[element setModel:model];
			[element setIsLocal:isLocal];
			success = [self setDimensionElement:element forModel:model error:error];
			if (!success) {
				[element release];
				break;
			}
			[model addDimension:element error:error];
			[element release];
				
		} while ([parser testSymbol:@","]);
			
	} else if ([parser testSymbol:@"param"]) {
		do {
			EXPParameterElement *element = [[EXPParameterElement alloc] init];
			[element setModel:model];
			[element setIsLocal:isLocal];
			success = [self setParameterElement:element forModel:model error:error];
			[model addParameter:element error:error];
			[element release];
				
		} while ([parser testSymbol:@","]);
			
	} else if ([parser testSymbol:@"aux"] || [parser testSymbol:@"auxiliary"]) {
		do {
			EXPAuxiliaryElement *element = [[EXPAuxiliaryElement alloc] init];
			[element setModel:model];
			[element setIsLocal:isLocal];
			success = [self setAuxiliaryElement:element forModel:model error:error];
			[model addAuxiliary:element error:error];
			[element release];
				
		} while ([parser testSymbol:@","]);
			
	} else if ([parser testSymbol:@"index"]) {
		do {
			EXPAuxiliaryElement *element = [[EXPAuxiliaryElement alloc] init];
			[element setModel:model];
			[element setIsLocal:isLocal];
			success = [self setIndexElement:element forModel:model error:error];
			[model addAuxiliary:element error:error];
			[element release];
				
		} while ([parser testSymbol:@","]);
			
	} else if ([parser testSymbol:@"var"] || [parser testSymbol:@"state"]) {
		do {
			EXPStateElement *element = [[EXPStateElement alloc] init];
			[element setModel:model];
			[element setIsLocal:isLocal];
			[element setExpressionType:doubleType];
			success = [self setStateElement:element forModel:model error:error];
			[model addVariable:element error:error];
			[element release];
				
		} while ([parser testSymbol:@","]);
			
	} else if ([parser testSymbol:@"table"]) {
		EXPTableElement *element = [[EXPTableElement alloc] init];
		success = [self setTableElement:element forModel:model error:error];
		[element release];
		
	} else if ([parser testSymbol:@"on"]) {
		EXPSwitchElement *element = [[EXPSwitchElement alloc] init];
		[element setModel:model];
		[element setIsLocal:isLocal];
		success = [self setSwitchElement:element forModel:model error:error];
		[model addSwitch:element error:error];
		[element release];
							
	} else if ([parser testSymbol:@"option"]) {
		do {
			success = [self processOption:model error:error];
		} while ([parser testSymbol:@","]);

	} else if ([parser testSymbol:@"print"]) {
		do {
			EXPPrintElement *element = [[EXPPrintElement alloc] init];
			[element setModel:model];
			[element setIsLocal:isLocal];
			success = [self setPrintElement:element forModel:model error:error];
			[model addOutputter:element error:error];
			[element release];
		} while ([parser testSymbol:@","]);
			
	} else if ([parser testSymbol:@"plot"]) {
		do {
			EXPPlotElement *element = [[EXPPlotElement alloc] init];
			[element setModel:model];
			[element setIsLocal:isLocal];
			success = [self setPlotElement:element forModel:model error:error];
			[model addOutputter:element error:error];
			[element release];
		} while ([parser testSymbol:@","]);
			
	} else if ([parser testSymbol:@"function"]) {
/*		symbolTable = [[EXPSymbolTable alloc] init];
		[symbolTable setParentTable:parentSymbolTable];

		[symbolTable release]; */
		EXPUserFunctionElement *element = [[EXPUserFunctionElement alloc] init];
		[element setModel:model];
		success = [self setUserFunctionElement:element forModel:model error:error];
		[model addFunction:element error:error];
		[element release];
	
	} else if ([parser testSymbol:@"for"]) {
		EXPForLoopStatement *element = [[EXPForLoopStatement alloc] init];
		[self setForElement:element forModel:model error:error];
		if (success) {
			if ([[element block] hasStatements]) {
				[model addStatement:element error:error];
			} else {
				[model addInitialiser:element error:error];
			}
		}
		[element release];
	
//	} else if ([parser testSymbol:@"foreach"]) {
	
	} else if ([parser testSymbol:@"if"]) {
		EXPIfThenElseStatement *element = [[EXPIfThenElseStatement alloc] init];
		[element setModel:model];
		[element setIsLocal:isLocal];
		success = [self setIfThenElseElement:element forModel:model error:error];
		if (success) {
			[model addStatement:element error:error];
		}
		[element release];
	
	} else if ([parser testSymbol:@"while"]) {
		EXPWhileStatementElement *element = [[EXPWhileStatementElement alloc] init];
		[element setModel:model];
		[element setIsLocal:isLocal];
		success = [self setWhileElement:element forModel:model error:error];
		if (success) {
			[model addStatement:element error:error];
		}
		[element release];
	
	} else {
		symbolTable = parentSymbolTable;
		[self assignment:model error:error];
	
	}
	
	if(!([parser isEndOfLine] || [parser isEndOfFile] || [parser testSymbol:@";"])) {
		success = [error addError:@"End of line or semicolon expected" atLine:[parser lineNumber]];
	}

	return success;
}

- (BOOL) loadModel:(EXPBlockElement *)model
{
	BOOL success = YES;
	EXPSymbolTable *symbolTable = [model symbolTable];
	EXPParser *parser = [self parser];
	EXPError *error = [self error];

	[self registerDefaultFunctions:model];
	[self declareBuiltinVariables:model];

	while (![parser isEndOfFile] && success) {
		success = [self parseStatement:model symbolTable:symbolTable destination:model isLocal:NO error:error];

	}

//	Save constant & variable names & labels to the model.

	return success;
}

- (void) dealloc
{
	[super dealloc];
}

@end