//
//  EXPMachineGenerator.m
//  dde
//
//  Created by ashley on 17/05/2008.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "EXPSymbolTable.h"
#import "EXPMachineGenerator.h"
#import "EXPModel.h"
#import "EXPElement.h"
#import "EXPDimensionElement.h"
#import "EXPIndexElement.h"
#import "EXPParameterElement.h"
#import "EXPAuxiliaryElement.h"
#import "EXPStateElement.h"
#import "EXPBlockElement.h"
#import "EXPStatementBlockElement.h"
#import "EXPFunctionElement.h"
#import "EXPSymbolReference.h"
#import "EXPOutputElement.h"
#import "EXPPrintElement.h"
#import "EXPSwitchElement.h"
#import "EXPUserFunctionElement.h"
#import "EXPIfThenElseStatement.h"
#import "EXPForLoopStatement.h"
#import "EXPWhileStatementElement.h"
#import "EXPTableElement.h"
#import "EXPAssignmentOp.h"
#import "EXPConstant.h"
#import "EXPUnaryOp.h"
#import "EXPBinOp.h"
#import "EXPTernOp.h"
#import "EXPOutputItemOp.h"
#import "EXPFormalArgumentElement.h"
#import "EXPVirtualMachine.h"
#import "EXPDimensionItemElement.h"
#import "EXPTableDefinitionOp.h"
#import "EXPError.h"

NSDictionary *unaryOpcodes = nil;
NSDictionary *binaryOpcodes = nil;
NSDictionary *functionOpcodeList = nil;

@implementation EXPMachineGenerator

- (id) init
{
	if ([super init]!=nil) {
		_opcodeList = nil;
	}
	return self;
}

- (void) setEcho:(BOOL)echo
{
	_echo = echo;
}

- (BOOL) echo
{
	return _echo;
}

- (BOOL) compileIndexLoopHeader:(NSArray *)indices nIndices:(int)nIndices machine:(EXPVirtualMachine *)machine branchAddresses:(unsigned int *)branchAddresses 
	loopAddresses:(unsigned int *)loopAddresses

{
	int i;
	for(i=0; i<nIndices; i++) {
		EXPDimensionElement *element = [indices objectAtIndex:i];
//			NSLog(@"%@", element);
		[machine putOpcode:LDA mode:IMM+INTEGER];
		[machine putUInt:0];
		[machine putOpcode:STA mode:GLOBAL+INTEGER];
		[machine putUInt:[element address]];
		loopAddresses[i] = [machine ptr];
		[machine putOpcode:LDA mode:GLOBAL+INTEGER];
		[machine putUInt:[element address]];
		[machine putOpcode:GTH mode:IMM+INTEGER];
		[machine putUInt:[element upperBound]];
		[machine putOpcode:BNE mode:LONG];
		branchAddresses[i] = [machine ptr];
		[machine putUInt:0];
	}
	
	return YES;
}

// - (BOOL) compileIndexLoopFooter(EXPVirtualMachine *machine, int nIndices, NSArray *indices, unsigned int *branchAddresses, unsigned int *loopAddresses)
- (BOOL) compileIndexLoopFooter:(NSArray *)indices nIndices:(int)nIndices machine:(EXPVirtualMachine *)machine branchAddresses:(unsigned int *)branchAddresses 
	loopAddresses:(unsigned int *)loopAddresses
{
	int i;
	for(i=nIndices-1; i>=0; i--) {
		EXPDimensionElement *element = [indices objectAtIndex:i];
		[machine putOpcode:INC mode:GLOBAL+INTEGER];
		[machine putUInt:[element address]];
		[machine putOpcode:BRA mode:LONG];
		[machine putUInt:loopAddresses[i]];
	}

	unsigned int here = [machine ptr];
	for(i=0; i<[indices count]; i++) {
		[machine putUInt:here at:branchAddresses[i]];
	}

	return YES;
}

- (BOOL) compileIndex:(EXPIndexElement *)index machine:(EXPVirtualMachine *)machine error:(EXPError *)err
{
	BOOL success = YES;

	[machine putOpcode:LDA mode:GLOBAL+INTEGER];
	[machine putUInt:[index address]];

	return success;
}

- (BOOL) compileIndexOffset:(EXPSymbolReference *)reference machine:(EXPVirtualMachine *)machine dimensions:(NSArray *)dimensions baseAddress:(int)baseAddress 
	address:(int *)variableAddress error:(EXPError *)err
{
	EXPDataElement *element = [reference element];
//	NSLog(@"[element class] = %@", [element class]);
	if (![element respondsToSelector:@selector(dimensions)]) {
		return YES;
	}
//	NSArray *elementDimensions = [element dimensions];
	
	*variableAddress = baseAddress;
/*	if ([dimensions count]>0) {
		EXPElement *item = [dimensions objectAtIndex:0];
		EXPDimensionElement *dimension = [dimensions objectAtIndex:0];
		int index;
		NSString *itemType = [item elementType];
		EXPExpression *indexExp = nil;
	
		if ([itemType isEqualToString:@"dimension"]) {
			index = [item address];
			indexExp = [[EXPIndexElement alloc] init];
			[(EXPIndexElement *)indexExp setAddress:index];
			knowAddress = NO;
		} else if ([itemType isEqualToString:@"dimensionItem"]) {
//			index = [dimension indexOfItem:item];
//			index = [[elementDimensions objectAtIndex:0] indexOfItem:item];
//			indexExp = [[EXPConstant alloc] initWithName:@""];
//			[indexExp setValue:index];
			index =  [(EXPDimensionItemElement *)item index];
			
		} else if ([item isKindOfClass:[EXPExpression class]]) {

		} else {
		}

		int i;
		for(i=1; i<[dimensions count]; i++) {
//			item = [dimensions objectAtIndex:i];
			item = [elementDimensions objectAtIndex:i];
			dimension = [dimensions objectAtIndex:i];
			index = [dimension indexOfItem:item];
		}
	
		if (knowAddress) {
			*variableAddress += [(EXPConstant *)indexExp value];
		} else {
//			[indexExp compile:machine error:err];
			[self compileIndex:(EXPIndexElement *)indexExp machine:machine error:err];
		}

		[indexExp release];
	} */
	EXPExpression *indexExp = nil;

	int i;
	for(i=0; i<[dimensions count]; i++) {
		EXPExpression *item = [dimensions objectAtIndex:i];
		NSString *itemType = [item elementType];

		EXPExpression *exp;
		if ([itemType isEqualToString:@"dimension"]) {
//			NSLog(@"[item class] = %@", [item class]);
//			EXPSymbolReference *ref = [[EXPSymbolReference alloc] init];
//			[ref setElement:item];
//			exp = (EXPSymbolReference *)ref;
			exp = [item retain];
		} else if ([itemType isEqualToString:@"dimensionItem"]) {
			int index = [[(EXPSymbolReference *)item element] index];
			exp = [[EXPConstant alloc] initWithName:@""];
			[exp setValue:index];
		} else if ([item isKindOfClass:[EXPExpression class]]) {
			exp = (EXPExpression *)[item retain];
		} else {
			NSLog(@"OOoppssss!!!!");
		}
		
/*		int dimSize = [[[reference element] class] storageSize];
		EXPConstant *dimSizeConst = [[EXPConstant alloc] initWithName:@""];
		[dimSizeConst setValue:dimSize];
		EXPBinOp *pp = [[EXPBinOp multOpWithLeft:exp andRight:dimSizeConst isMultOp:YES] retain];
		[dimSizeConst release];
		[exp release];
		exp = pp; */

		if (indexExp==nil) {
			indexExp = exp;
		} else {
/*			EXPDimensionElement *dimension = [elementDimensions objectAtIndex:(i-1)];
			int p = [dimension nElements];
			EXPConstant *n = [[EXPConstant alloc] initWithName:@""];
			[n setValue:p];
			EXPBinOp *op = [EXPBinOp multOpWithLeft:pp andRight:n isMultOp:YES];
			[pp release];
			[n release];
			EXPBinOp *op2 = [[EXPBinOp addOpWithLeft:op andRight:exp isAddOp:YES] retain];
			[exp release];
			[indexExp release];
			
			indexExp = op2; */
		}

	}
	
	BOOL knowAddress = (indexExp==nil) || [[indexExp elementType] isEqualToString:@"constant"];
	if (knowAddress) {
		int dimSize = [[[reference element] class] storageSize];
		*variableAddress += [(EXPConstant *)indexExp value]*dimSize;
	} else {
		[self compileExpression:indexExp machine:machine error:err];
	}

	[indexExp release];
	
	return knowAddress;
}

- (BOOL) compileIndexOffset:(EXPSymbolReference *)reference machine:(EXPVirtualMachine *)machine baseAddress:(int)baseAddress address:(int *)variableAddress 
	error:(EXPError *)error
{
	NSArray *dimensions = [reference dimensions];
//	NSArray *dimensions = [reference indices];
	return [self compileIndexOffset:reference machine:machine dimensions:dimensions baseAddress:baseAddress address:variableAddress error:error];
}

- (BOOL) compileConstant:(EXPConstant *)constant machine:(EXPVirtualMachine *)machine error:(EXPError *)err opcode:(unsigned int)opcode
{
	EXPExpressionType constantType = [constant expressionType];
	unsigned int dataType = (constantType==doubleType)?FLTPOINT:INTEGER;
	[machine putOpcode:opcode mode:(dataType + IMM)];
	if (constantType==doubleType) {
		[machine putDouble:[constant value]];
	} else {
		[machine putUInt:(unsigned int)[constant value]];
	}
	
	return YES;
} 

- (BOOL) compileElement:(EXPElement *)element machine:(EXPVirtualMachine *)machine withArguments:(NSArray *)arguments dimensions:(NSArray *)dimensions 
		 error:(EXPError *)error opcode:(unsigned int)opcode mode:(unsigned int)mode 
		 address:(unsigned int)address
{
	BOOL success = YES;
	
	NSString *elementType = [element elementType];
	
	if ([elementType isEqualToString:@"function"]) {
		if (functionOpcodeList==nil) {
			functionOpcodeList = [NSDictionary dictionaryWithObjectsAndKeys:
				[NSNumber numberWithInt:ZIDZ], @"zidz",
				[NSNumber numberWithInt:XIDZ], @"xidz",
				
				[NSNumber numberWithInt:MINN], @"min",
				[NSNumber numberWithInt:MAXN], @"max",
				[NSNumber numberWithInt:MEANN], @"mean",
				[NSNumber numberWithInt:MOD], @"mod",
				
				[NSNumber numberWithInt:STEP], @"step",
				[NSNumber numberWithInt:RAMP], @"ramp",
				
				[NSNumber numberWithInt:RDUNI], @"urand",
				[NSNumber numberWithInt:RDNRM], @"nrand",
				[NSNumber numberWithInt:RDEXP], @"xrand",
				
				nil];
		}
		EXPFunctionElement *function = (EXPFunctionElement *)element;
		BOOL variableArgs = [function variableArgs];
		BOOL isBuiltin = [function isBuiltin];
		int numArgs = [arguments count];
	
		if (!isBuiltin) {
		}
	
		if (variableArgs || !isBuiltin) {
			int i;
			for(i=0; (i<numArgs) && success; i++) {
				EXPExpression *argument = [arguments objectAtIndex:i];
				if ([argument isSimple]) {
					success = [self compileExpression:argument machine:machine error:error opcode:PSH];
				} else {
					success = [self compileExpression:argument machine:machine error:error];
					[machine putOpcode:PSHA mode:0];
				}
			}
		} else {
			if (numArgs>0) {
				EXPExpression *argument = [arguments objectAtIndex:0];
				success = [self compileExpression:argument machine:machine error:error];
			}
		}
	
		if (success) {	
			if (isBuiltin) {
//				unsigned int opcode = [element address];
				NSNumber *num = [functionOpcodeList objectForKey:[function name]];
				unsigned int opcode = [num intValue];
				[machine putOpcode:opcode mode:0];
			
				if (variableArgs) {
					[machine putUInt:(unsigned int)[arguments count]];
				}
			} else {
			}
		}
	} else if ([elementType isEqualToString:@"table"]) {
		if ([arguments count]!=1) {
			return NO;
		}
	
		EXPExpression *exp = [arguments objectAtIndex:0];
		[self compileExpression:exp machine:machine error:error];

		if ([dimensions count]>0) {
			return NO;
		}
		[machine putOpcode:INTERP mode:GLOBAL+FLTPOINT];
		[machine putUInt:[element address]];
	} else if ([elementType isEqualToString:@"variable"] && !((arguments==nil) || ([arguments count]==0))) {
//		if ((arguments==nil) || ([arguments count]==0)) {
//			success = [super compile:machine withArguments:nil dimensions:nil error:error opcode:opcode mode:mode address:address];
//		} else {
		EXPStateElement *state = (EXPStateElement *)element;
		if (![state isDelayVariable]) {
			NSString *message = [[NSString alloc] initWithFormat:@"Non-delay variable with delay argument! %@\n", [state name]];
			[error addError:message atLine:-1];
			[message release];
			return NO;
		}
		
		int delayNumber = [state delayNumber];
		
		int delayOpcode = [[[self opcodeList] objectForKey:@"delayTrapCode"] intValue];
//		BOOL knowAddress = compileIndexOffset(machine, dimensions, delayNumber, &delayNumber, error);
		BOOL knowAddress = [self compileIndexOffset:nil machine:machine dimensions:dimensions baseAddress:delayNumber address:&delayNumber error:error];
		if (!knowAddress) {
			delayOpcode = [[[self opcodeList] objectForKey:@"delay1TrapCode"] intValue];
			[machine putOpcode:PSHA mode:INTEGER];
		}
		
		EXPExpression *delayArg = [arguments objectAtIndex:0];
		if ([delayArg isSimple]) {
			success = [self compileExpression:delayArg machine:machine error:error opcode:PSH];
		} else {
			success = [self compileExpression:delayArg machine:machine error:error];
			[machine putOpcode:PSHA mode:0];
		}
			
		if ([arguments count]>1) {
			EXPExpression *initialArg = [arguments objectAtIndex:1];
			success = [self compileExpression:initialArg machine:machine error:error];
			[machine putOpcode:PSHA mode:0];
		} else {
			[machine putOpcode:PSH mode:IMM];
			[machine putDouble:0.0];
		}
		
		if ([arguments count]>2) {
			EXPExpression *lagArg = [arguments objectAtIndex:2];
			success = [self compileExpression:lagArg machine:machine error:error];
			[machine putOpcode:PSHA mode:0];
		} else {
			[machine putOpcode:PSH mode:IMM];
			[machine putDouble:0.0];
		}
		
		[machine putOpcode:LDA mode:INTEGER+IMM];
		[machine putUInt:delayNumber];
		[machine putOpcode:TRAP mode:0];
		[machine putUInt:delayOpcode];
		
//		}
	} else if ([elementType isEqualToString:@"userfunction"]) {
		int numArgs = [arguments count];
		[machine putOpcode:SAVE mode:0];
		int i;
		for(i=0; (i<numArgs) && success; i++) {
			EXPExpression *argument = [arguments objectAtIndex:i];
			if ([argument isSimple]) {
				success = [self compileExpression:argument machine:machine error:error opcode:PSH];
			} else {
				success = [self compileExpression:argument machine:machine error:error];
				[machine putOpcode:PSHA mode:0];
			}
		}

		[machine putOpcode:BSR mode:LONG];
		[machine putUInt:[element address]];

	} else {
		if ((arguments==nil) || ([arguments count]==0)) {
			[machine putOpcode:opcode mode:mode];
			[machine putUInt:address];
		} else {
			[error addError:[NSString stringWithFormat:@"Element %@ has argument(s)", [element name]] atLine:-999999];
		}
	}

	return success;
}

- (BOOL) compileSymbolReference:(EXPSymbolReference *)ref machine:(EXPVirtualMachine *)machine error:(EXPError *)err opcode:(unsigned int)opcode
{
	BOOL success = YES;

	int addressingMode = 0;
	int address = 0;
	EXPElement *element = [ref element];
	int addr = (int)[element address];
	BOOL knowAddress = [self compileIndexOffset:ref machine:machine baseAddress:addr address:&address error:err];
	if (knowAddress) {
		addressingMode = ([element isLocal])?LOCAL:GLOBAL;
	} else {
		addressingMode = ([element isLocal])?INDXL:INDXG;
	}
	
//	EXPElement *element = [ref element];
	NSArray *arguments = [ref arguments];
	EXPExpressionType elementType = [element expressionType];
	unsigned int dataType = (elementType==doubleType)?FLTPOINT:INTEGER;
	int mode = (addressingMode + dataType);
	[self compileElement:element machine:machine withArguments:arguments dimensions:[ref dimensions] error:err opcode:opcode mode:mode address:address];

	return success;
} 

- (BOOL) compileExpression:(EXPExpression *)exp machine:(EXPVirtualMachine *)machine error:(EXPError *)error opcode:(int)opcode
{
	BOOL success = YES;
	
	if ([exp isMemberOfClass:[EXPUnaryOp class]]) {
		EXPUnaryOp *op = (EXPUnaryOp *)exp;
		EXPExpression *left = [op left];
		success =[self compileExpression:left machine:machine error:error];
		int opcode = [[unaryOpcodes objectForKey:[op name]] intValue];
		if (opcode!=0x0) {
			[machine putOpcode:opcode mode:FLTPOINT];
		}
	} else if ([exp isMemberOfClass:[EXPBinOp class]]) {
		EXPBinOp *op = (EXPBinOp *)exp;
		EXPExpression *left = [op left];
		EXPExpression *right = [op right];

//		if (opcode==0x0) {
//			NSLog(@"Oops! Don't know how to compile opcode...");
//		NSString *name = [op name];
		if ([right isSimple]) {
			[self compileExpression:left machine:machine error:error];
			[self compileExpression:right machine:machine error:error opcode:opcode];
		} else {
			[self compileExpression:right machine:machine error:error];
			[machine putOpcode:STA mode:AUTO];
			[self compileExpression:left machine:machine error:error];
			[machine putOpcode:opcode mode:AUTODEC];
		}
	} else if ([exp isMemberOfClass:[EXPTernOp class]]) {
//		[self compileTernaryOp:(EXPTernOp *)exp machine:machine error:error opcode:opcode];
		EXPTernOp *op = (EXPTernOp *)exp;
		EXPExpression *left = [op left];
		EXPExpression *right = [op right];
		EXPExpression *third = [op third];
	
		[self compileExpression:third machine:machine error:error];
		[machine putOpcode:BEQ mode:LONG];
		unsigned int branch1 = [machine ptr];
		[machine putUInt:0];
		
		[self compileExpression:left machine:machine error:error];
		[machine putOpcode:BRA mode:LONG];
		unsigned int branch2 = [machine ptr];
		[machine putUInt:0];
	
		[machine putUInt:[machine ptr] at:branch1];
		[self compileExpression:right machine:machine error:error];
		
		[machine putUInt:[machine ptr] at:branch2];
	} else if ([exp isMemberOfClass:[EXPConstant class]]) {
		[self compileConstant:(EXPConstant *)exp machine:machine error:error opcode:opcode];
	} else if ([exp isMemberOfClass:[EXPSymbolReference class]]) {
		[self compileSymbolReference:(EXPSymbolReference *)exp machine:machine error:error opcode:opcode];
	} else if ([exp isMemberOfClass:[EXPDimensionElement class]]) {
		NSLog(@"EXPDimensionElement");
		EXPDimensionElement *dim = (EXPDimensionElement *)exp;
		unsigned int mode = GLOBAL + INTEGER;
		[self compileElement:dim machine:machine withArguments:nil dimensions:nil error:error opcode:LDA mode:mode address:[dim address]];
	} else {
		success = NO;
	}

	return success;
}

- (BOOL) compileExpression:(EXPExpression *)exp machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
		
	if ([exp isMemberOfClass:[EXPUnaryOp class]]) {
		if (unaryOpcodes==nil) {
			unaryOpcodes = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:NEGA], @"-",  
				[NSNumber numberWithInt:0x0], @"+",  
				[NSNumber numberWithInt:ABSA], @"abs",
				[NSNumber numberWithInt:SQRTA], @"sqrt",
				[NSNumber numberWithInt:INTA], @"int",
				[NSNumber numberWithInt:FLTA], @"float", 
				[NSNumber numberWithInt:EXPA], @"exp",
				[NSNumber numberWithInt:LNA], @"log",
				[NSNumber numberWithInt:LOG10A], @"log10",
				[NSNumber numberWithInt:SINA], @"sin",
				[NSNumber numberWithInt:COSA], @"cos",
				[NSNumber numberWithInt:TANA], @"tan",
				[NSNumber numberWithInt:SINA], @"asin",
				[NSNumber numberWithInt:ACOSA], @"acos",
				[NSNumber numberWithInt:ATANA], @"atan",
				[NSNumber numberWithInt:SINHA], @"sinh",
				[NSNumber numberWithInt:COSHA], @"cosh",
				[NSNumber numberWithInt:TANHA], @"tanh",
				[NSNumber numberWithInt:ASINHA], @"asinh",
				[NSNumber numberWithInt:ACOSHA], @"acosh",
				[NSNumber numberWithInt:ATANHA], @"atanh",
				[NSNumber numberWithInt:ERFA], @"erf",
				nil];
		}
		EXPUnaryOp *op = (EXPUnaryOp *)exp;
		NSString *name = [op name];
		int opcode = [[unaryOpcodes objectForKey:name] intValue];
		if (opcode!=0x0) {
			success = [self compileExpression:exp machine:machine error:error opcode:opcode];
		}
	} else if ([exp isMemberOfClass:[EXPBinOp class]]) {
		if (binaryOpcodes==nil) {
			binaryOpcodes = [NSDictionary dictionaryWithObjectsAndKeys:
				[NSNumber numberWithInt:ADD], @"+", 
				[NSNumber numberWithInt:SUB], @"-",  
				[NSNumber numberWithInt:MLT], @"*",  
				[NSNumber numberWithInt:DVD], @"/",  
				[NSNumber numberWithInt:PWR], @"^",  
				[NSNumber numberWithInt:GTH], @">",  
				[NSNumber numberWithInt:LTH], @"<",  
				[NSNumber numberWithInt:GTE], @">=",  
				[NSNumber numberWithInt:LTE], @"<=",  
				[NSNumber numberWithInt:EQL], @"==",  
				[NSNumber numberWithInt:NEQ], @"!=",  
				nil];
		}
		EXPBinOp *op = (EXPBinOp *)exp;
		NSString *name = [op name];
		if ([name isEqualToString:@"and"]) {
			success = [self compileExpression:[op left] machine:machine error:error];
			if (!success) {
				return NO;
			}
	
			[machine putUInt:BEQ];
			unsigned int branchpc = [machine pc];
			[machine putUInt:0];
		
			success = [self compileExpression:[op right] machine:machine error:error];
			if (!success) {
				return NO;
			} 
	
			[machine putUInt:[machine pc] at:branchpc];
		} else if ([name isEqualToString:@"or"]) {
			success = [self compileExpression:[op left] machine:machine error:error];
			if (!success) {
				return NO;
			}
	
			[machine putUInt:BNE];
			unsigned int branchpc = [machine pc];
			[machine putUInt:0];
	
			success = [self compileExpression:[op right] machine:machine error:error];
			if (!success) {
				return NO;
			}
	
			[machine putUInt:[machine pc] at:branchpc];
		} else {
			int opcode = [[binaryOpcodes objectForKey:name] intValue];
			success = [self compileExpression:exp machine:machine error:error opcode:opcode];
		}
//	} else if ([exp isKindOfClass:[EXPConstant class]]) {
//		[self compileConstant:(EXPConstant *)exp machine:machine error:error opcode:opcode];
	} else {
		success = [self compileExpression:exp machine:machine error:error opcode:LDA];
//		success = NO;
	}
	
	return success;
}

- (BOOL) compileAssignmentOp:(EXPAssignmentOp *)assignment machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	EXPSymbolReference *leftReference = [assignment left];
	EXPDataElement *left = [leftReference element];
	NSArray *leftDimensions = [left dimensions];
	NSArray *right = [assignment right];
	int rightCount = [right count];
	int primes = [leftReference primes];
	unsigned int address;

	if (rightCount==1) {
//		NSArray *indices = [assignment indices];
		NSArray *indices = [leftReference indices];
		int nIndices = [indices count];
		unsigned int *branchAddresses = (unsigned int *)calloc(nIndices, sizeof(unsigned int));
		unsigned int *loopAddresses = (unsigned int *)calloc(nIndices, sizeof(unsigned int));
		
//		compileIndexLoopHeader(machine, nIndices, indices, branchAddresses, loopAddresses);
		success = [self compileIndexLoopHeader:indices nIndices:nIndices machine:machine branchAddresses:branchAddresses loopAddresses:loopAddresses];
	
		int address = 0;
		int baseAddress = (primes==0)?[left address]:[left gradientAddress];
		BOOL knowAddress = [self compileIndexOffset:leftReference machine:machine baseAddress:baseAddress address:&address error:error];
		if (!knowAddress) {
			int mode = [left isLocal]?INDXL:INDXG;
			[machine putOpcode:PEA mode:mode+FLTPOINT];
			[machine putUInt:address];
		}
		
		EXPExpression *exp = [right objectAtIndex:0];
		success = [self compileExpression:exp machine:machine error:error];
		
		int accessMode = [left isLocal]?LOCAL:GLOBAL;
		int addressingMode = knowAddress?accessMode:AUTOIDC;
		EXPExpressionType expressionType = [exp expressionType];
		unsigned int dataType = (expressionType==doubleType)?FLTPOINT:INTEGER;
		[machine putOpcode:STA mode:(addressingMode + dataType)];
		if (knowAddress) {
			[machine putUInt:address];
		}
		
//		compileIndexLoopFooter(machine, nIndices, indices, branchAddresses, loopAddresses);
		success = [self compileIndexLoopFooter:indices nIndices:nIndices machine:machine branchAddresses:branchAddresses loopAddresses:loopAddresses];

		free(branchAddresses);
		free(loopAddresses);
		
	} else {
		int nDimensions = [leftDimensions count];
		int totalDimensions = 1;
		int i;
		for(i=0; i<nDimensions; i++) {
			EXPDimensionElement *dimension = [leftDimensions objectAtIndex:i];
			int n = [dimension nElements];
			totalDimensions *= n;
		}
		
		if (totalDimensions!=rightCount) {
			NSString *message = [NSString stringWithFormat:@"Lengths of lefthand and righthand sides do not agree for %@", [left name]];
			[error addError:message atLine:-999999];
			return NO;
		}

		if (rightCount==1) {
			EXPExpression *exp = [right objectAtIndex:0];
			success = [self compileExpression:exp machine:machine error:error];
			if (!success) {
				return success;
			}
				
			[machine putOpcode:LDA mode:INTEGER+IMM];
			[machine putUInt:([left numberOfElements] - 1)];
			unsigned int loop = [machine ptr];
			EXPExpressionType expressionType = [exp expressionType];
			unsigned int dataType = (expressionType==doubleType)?FLTPOINT:INTEGER;
			int mode = [left isLocal]?INDXL:INDXG;
			[machine putOpcode:STA mode:dataType+mode];
			address = [left address];
			[machine putUInt:address];
			[machine putOpcode:SUB mode:INTEGER+IMM];
			[machine putUInt:1];
			[machine putOpcode:BGE mode:LONG];
			[machine putUInt:loop];
				
		} else {
			int baseAddress = 0;
			if (primes==0) {
				baseAddress = [left address];
			} else {
				baseAddress = [left gradientAddress];
			}
		
			int i;
			for(i=0; i<rightCount; i++) {
				EXPExpression *exp = [right objectAtIndex:i];
				success = [self compileExpression:exp machine:machine error:error];
				if (!success) {
					break;
				}
				EXPExpressionType expressionType = [exp expressionType];
				unsigned int dataType = (expressionType==doubleType)?FLTPOINT:INTEGER;
				address = baseAddress + i*[[left class] storageSize];
				[machine putOpcode:STA mode:dataType+GLOBAL];
				[machine putUInt:address];
			}
		}

	}

	return success;
}

- (BOOL) compilePrintHeader:(EXPPrintElement *)printer machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	
	NSString *header = [printer header];
	if (header==nil) {
		return success;
	}
	
	int fileNumber = [printer fileNumber];
	int printOpcode;
	if (fileNumber<0) {
		printOpcode = PSTR;
	} else {
		printOpcode = FPSTR;
//		return NO;
	}

	[machine putOpcode:printOpcode mode:(INTEGER)];
	if (fileNumber>=0) {
		[machine putUInt:fileNumber];
	}
	
/*	int i;
	for(i=0; i<[header length]; i++) {
		unichar ch = [header characterAtIndex:i];
		[machine putUShInt:(unsigned int)ch];
	}
	[machine putUShInt:(unsigned int)'\n'];
	[machine putUShInt:(unsigned int)0x0]; */
	[machine putString:header withNewLine:YES];

	return success;
}

- (BOOL) compilePrintFooter:(EXPPrintElement *)printer machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	
	NSString *footer = [printer footer];
	if (footer==nil) {
		return success;
	}
	
	int fileNumber = [printer fileNumber];
	int printOpcode;
	if (fileNumber<0) {
		printOpcode = PSTR;
	} else {
		printOpcode = FPSTR;
	}

	[machine putOpcode:printOpcode mode:(INTEGER)];
	if (fileNumber>=0) {
		[machine putUInt:fileNumber];
	}
	
	[machine putString:footer withNewLine:YES];

	return success;
}

- (BOOL) compileOutputItem:(EXPOutputItemOp *)item forPrinter:(EXPPrintElement *)printer withSeparator:(BOOL)sep machine:(EXPVirtualMachine *)machine 
		 error:(EXPError *)error
{
	BOOL success = YES;
	
/*	int fileNumber = [printer fileNumber];
	int printOpcode;
	int printCharOpcode;
	int printStringOpcode;
	if (fileNumber<0) {
		printOpcode = PRTA;
		printCharOpcode = PCHR;
		printStringOpcode = PSTR;
	} else {
		printOpcode = FPRTA;
		printCharOpcode = FPCHR;
		printStringOpcode = FPSTR;
	}
	
	EXPExpression *exp = [item left];

	NSArray *indices = [item indices];
		
	unsigned int *branchAddresses;
	unsigned int *loopAddresses;
	int nIndices = [indices count];
	if (nIndices>0) {
		branchAddresses = (unsigned int *)calloc(nIndices, sizeof(unsigned int));
		loopAddresses = (unsigned int *)calloc(nIndices, sizeof(unsigned int));
		compileIndexLoopHeader(machine, nIndices, indices, branchAddresses, loopAddresses);
	}

	if (sep) {
		NSString *separator = [printer separator];
		if (separator==nil) {
			[machine putOpcode:printCharOpcode mode:(INTEGER + IMM)];
			[machine putUInt:(unsigned int)'\t'];
			if (fileNumber>=0) {
				[machine putUInt:fileNumber];
			}
		} else {
			[machine putOpcode:printStringOpcode mode:INTEGER];
			if (fileNumber>=0) {
				[machine putUInt:fileNumber];
			}
			[machine putString:separator withNewLine:NO];
		}
	}
	success = [self compileExpression:exp machine:machine error:error];

	[machine putOpcode:printOpcode mode:FLTPOINT];
	if (fileNumber>=0) {
		[machine putUInt:fileNumber];
	}
	

	if (nIndices>0) {
		compileIndexLoopFooter(machine, nIndices, indices, branchAddresses, loopAddresses);
		free(branchAddresses);
		free(loopAddresses);
	} */
	
	EXPExpression *exp = [item left];

	NSArray *indices = [item indices];
		
	unsigned int *branchAddresses;
	unsigned int *loopAddresses;
	int nIndices = [indices count];
	if (nIndices>0) {
		branchAddresses = (unsigned int *)calloc(nIndices, sizeof(unsigned int));
		loopAddresses = (unsigned int *)calloc(nIndices, sizeof(unsigned int));
//		compileIndexLoopHeader(machine, nIndices, indices, branchAddresses, loopAddresses);
		success = [self compileIndexLoopHeader:indices nIndices:nIndices machine:machine branchAddresses:branchAddresses loopAddresses:loopAddresses];
	}

	success = [self compilePush:exp machine:machine error:error];

	if (nIndices>0) {
//		compileIndexLoopFooter(machine, nIndices, indices, branchAddresses, loopAddresses);
		success = [self compileIndexLoopFooter:indices nIndices:nIndices machine:machine branchAddresses:branchAddresses loopAddresses:loopAddresses];
		free(branchAddresses);
		free(loopAddresses);
	}
	
	return success;
}

- (BOOL) compileOutputter:(EXPOutputElement *)outputter machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	
/*	if ([[outputter elementType] isEqualToString:@"printer"]) {
		EXPPrintElement *printer = (EXPPrintElement *)outputter;
		int fileNumber = [printer fileNumber];
		int printOpcode;
		int printCharOpcode;
		int printStringOpcode;
		if (fileNumber<0) {
			printOpcode = PRTA;
			printCharOpcode = PCHR;
			printStringOpcode = PSTR;
		} else {
			printOpcode = FPRTA;
			printCharOpcode = FPCHR;
			printStringOpcode = FPSTR;
		}
		
		NSString *leader = [printer leader];
		if (leader!=nil) {
			[machine putOpcode:printStringOpcode mode:INTEGER];
			if (fileNumber>=0) {
				[machine putUInt:fileNumber];
			}
			[machine putString:leader withNewLine:NO];
		}
	
		NSArray *items = [printer expressionList];
	
		int i;
		for(i=0; i<[items count]; i++) {
			BOOL sep = (i>0);
			EXPOutputItemOp *exp = [items objectAtIndex:i];
			[self compileOutputItem:exp forPrinter:printer withSeparator:sep machine:machine error:error];
		}

		NSString *trailer = [printer trailer];
		if (trailer!=nil) {
			[machine putOpcode:printStringOpcode mode:INTEGER];
			if (fileNumber>=0) {
				[machine putUInt:fileNumber];
			}
			[machine putString:trailer withNewLine:NO];
		}
	
		[machine putOpcode:printCharOpcode mode:(INTEGER + IMM)];
		[machine putUInt:(unsigned int)'\n'];
	
		if (fileNumber>=0) {
			[machine putUInt:fileNumber];
		}
	} */
	NSString *outputterType = [outputter elementType];
	if ([outputterType isEqualToString:@"printer"] || [outputterType isEqualToString:@"plotter"]) {
		EXPPrintElement *printer = (EXPPrintElement *)outputter;
		[printer setAddress:[machine ptr]];
		NSArray *items = [printer expressionList];
	
		int i;
		for(i=0; i<[items count]; i++) {
			BOOL sep = (i>0);
			EXPOutputItemOp *exp = [items objectAtIndex:i];
			[self compileOutputItem:exp forPrinter:printer withSeparator:sep machine:machine error:error];
		}

	}
	[machine putOpcode:HLT mode:0];
	
	return success;
}

- (BOOL) compilePush:(EXPExpression *)exp machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	
	if ([exp isConstant]) {
		[machine putOpcode:PSH mode:FLTPOINT+IMM];
		double val = [(EXPConstant *)exp value];
		[machine putDouble:val];
	} else {
		[self compileExpression:exp machine:machine error:error];
		[machine putOpcode:PSHA mode:0];
	}
	
	return success;
}

- (BOOL) compileTable:(EXPTableDefinitionOp *)ref machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	
	EXPTableElement *table  = [[ref left] element];
	NSArray *dimensions = [table dimensions];
	if ([dimensions count]>0) {
		return NO;
	} 
	
	int i;
	NSArray *xPoints = [table xPoints];
	NSArray *yPoints = [table yPoints];
	int nPoints = [yPoints count];
	BOOL isRegular = [table isRegular];
	
	if (!isRegular) {
		for(i=0; i<nPoints; i++) {
			EXPExpression *xExp = [xPoints objectAtIndex:i];
			[self compilePush:xExp machine:machine error:error];
		}
	}
	for(i=0; i<nPoints; i++) {
		EXPExpression *yExp = [yPoints objectAtIndex:i];
		[self compilePush:yExp machine:machine error:error];
	}
	
	if (isRegular) {
		EXPExpression *exp = [table xMin];
		if (exp==nil) {
			[machine putOpcode:PSH mode:FLTPOINT+IMM];
			[machine putDouble:0.0];
		} else {
			[self compilePush:exp machine:machine error:error];
		}
		
		exp = [table xMax];
		if (exp==nil) {
			[machine putOpcode:PSH mode:FLTPOINT+IMM];
			[machine putDouble:1.0];
		} else {
			[self compilePush:exp machine:machine error:error];
		}
	}
	
	unsigned int opcode = isRegular?RTABLE:TABLE;
	
	[machine putOpcode:opcode mode:FLTPOINT+GLOBAL];
	[machine putUInt:[table address]];
	[machine putUInt:nPoints];
	[machine putUShInt:[table interpType]];
	
	return success;
}

- (BOOL) compileAllocateTable:(EXPTableElement *)table machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	
	[machine putOpcode:ATAB mode:GLOBAL+FLTPOINT];
	[machine putUInt:[table address]];
	
	return success;
}


- (BOOL) compileReleaseTable:(EXPTableElement *)table machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	
	[machine putOpcode:RTAB mode:GLOBAL+FLTPOINT];
	[machine putUInt:[table address]];
	
	return success;
}

- (BOOL) compileBlock:(EXPStatementBlockElement *)switcher machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;

	int i;
	NSArray *statements = [switcher statements];
	for(i=0; i<[statements count]; i++) {
		EXPElement *statement = [statements objectAtIndex:i];
		[self compileStatement:statement machine:machine error:error];
	} 

	return success;
}

- (BOOL) compileSwitchHeader:(EXPSwitchElement *)switcher machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;

	EXPExpression *header = [switcher header];
	[self compileExpression:header machine:machine error:error];

	return success;
}

- (BOOL) compileSwitchStatements:(EXPSwitchElement *)switcher machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;

/*	int i;
	NSArray *statements = [switcher statements];
	for(i=0; i<[statements count]; i++) {
		EXPElement *statement = [statements objectAtIndex:i];
		[self compileStatement:statement machine:machine error:error];
	} */
	EXPStatementBlockElement *block = [switcher block];
	[self compileBlock:block machine:machine error:error];
	
	return success;
}

- (BOOL) compileIfThenElseStatement:(EXPIfThenElseStatement *)statement machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;

	EXPExpression *header = [statement header];
	[self compileExpression:header machine:machine error:error];
	
	[machine putOpcode:BEQ mode:LONG];
	unsigned int branch1 = [machine ptr];
	[machine putUInt:0];
	
	EXPStatementBlockElement *block = [statement block];
	[self compileBlock:block machine:machine error:error];
	[machine putOpcode:BRA mode:LONG];
	unsigned int branch2 = [machine ptr];
	[machine putUInt:0];
	
	EXPStatementBlockElement *elseBlock = [statement elsePart];
	[machine putUInt:[machine ptr] at:branch1];
	[self compileBlock:elseBlock machine:machine error:error];
	
	[machine putUInt:[machine ptr] at:branch2];
	
	return success;
}

- (BOOL) compileForLoopStatement:(EXPForLoopStatement *)statement machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	
	EXPSymbolReference *index = [statement header];
	EXPAuxiliaryElement *indexElement = [index element];
	unsigned int mode = [indexElement isLocal]?LOCAL:GLOBAL;
	unsigned int address = [indexElement address];
	unsigned int dataType = INTEGER;
	
	EXPDimensionElement *inDimension = [[statement inDimension] element];
	if (inDimension==nil) {
		EXPExpression *fromExp = [statement fromExp];
		success = [self compileExpression:fromExp machine:machine error:error];
	} else {
		unsigned int lowerbound;
		if ([inDimension isOrderedDimension]) {
			lowerbound = [inDimension lowerBound];
		} else {
			lowerbound = 0;
		}
		[machine putOpcode:LDA mode:(IMM + dataType)];
		[machine putUInt:lowerbound];
	}

		
	[machine putOpcode:STA mode:(mode + dataType)];
	[machine putUInt:address];
		
	unsigned int loopAddress = [machine ptr];
	EXPExpression *toExp;
	NSString *comparisonOp;
	if (inDimension==nil) {
		toExp = [[statement toExp] retain];
		comparisonOp = @"<=";
	} else {
		int upperbound;
		if ([inDimension isOrderedDimension]) {
			upperbound = [inDimension upperBound];
		} else {
			upperbound = [inDimension nElements];
		}
		comparisonOp = @"<";
		toExp = [[EXPConstant alloc] initWithName:nil];
		[toExp setValue:upperbound];
//		toExp = [[EXPSymbolReference alloc] init];
//		[(EXPSymbolReference *)toExp setElement:constant];
	}

	EXPExpression *op = [[EXPBinOp alloc]  initOp:comparisonOp withLeft:index AndRight:toExp];
	success = [self compileExpression:op machine:machine error:error];
	[op release];
	
	[machine putOpcode:BEQ mode:LONG];
	unsigned int branch1 = [machine ptr];
	[machine putUInt:0];
		
	EXPStatementBlockElement *block = [statement block];
	[self compileBlock:block machine:machine error:error];
		
	[machine putOpcode:INC mode:(mode + dataType)];
	[machine putUInt:address];
	[machine putOpcode:BRA mode:LONG];
	[machine putUInt:loopAddress];
		
	[machine putUInt:[machine ptr] at:branch1];

	[toExp release];
	
	return success;
}

- (BOOL) compileWhileLoopStatement:(EXPWhileStatementElement *)statement machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;

	unsigned int loopAddress = [machine ptr];
	EXPExpression *header = [statement header];
	[self compileExpression:header machine:machine error:error];
	
	[machine putOpcode:BEQ mode:LONG];
	unsigned int branch1 = [machine ptr];
	[machine putUInt:0];
	
	EXPStatementBlockElement *block = [statement block];
	[self compileBlock:block machine:machine error:error];
	[machine putOpcode:BRA mode:LONG];
	[machine putUInt:loopAddress];
	
	[machine putUInt:[machine ptr] at:branch1];

	return success;
}

- (BOOL) compileStatement:(id)statement machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;

	if ([self echo]) {
		printf("%s\n", [[statement description] UTF8String]);
	}
	NSString *elementType = [statement elementType];
	if ([elementType isEqualToString:@"assignment"]) {
		success = [self compileAssignmentOp:(EXPAssignmentOp *)statement machine:machine error:error];
	} else if ([elementType isEqualToString:@"ifthenelse"]) {
		success = [self compileIfThenElseStatement:(EXPIfThenElseStatement *)statement machine:machine error:error];
	} else if ([elementType isEqualToString:@"forloop"]) {
		success = [self compileForLoopStatement:(EXPForLoopStatement *)statement machine:machine error:error];
//	} else if ([elementType isEqualToString: @"expression"] || [elementType isEqualToString: @"binaryop"]) {
//		success = [self compileExpression:(EXPExpression *)statement machine:machine error:error];
	} else if ([elementType isEqualToString:@"table"]) {
		success = [self compileTable:(EXPTableDefinitionOp *)statement machine:machine error:error];
	} else if ([elementType isEqualToString:@"whiledo"]) {
		success = [self compileWhileLoopStatement:(EXPWhileStatementElement *)statement machine:machine error:error];
	} else if ([elementType isEqualToString:@"statement"]) {
	} else {
		success = [self compileExpression:(EXPExpression *)statement machine:machine error:error];
	}

	return success;
}

- (BOOL) generateFunctionDefinition:(EXPUserFunctionElement *)function machine:(EXPVirtualMachine *)machine error:(EXPError *)error
{
	BOOL success = YES;
	int nextAddress = 0;
	
	int i;
	NSArray *arguments = [function formalArguments];
	for(i=0; i<[arguments count]; i++) {
		EXPFormalArgumentElement *arg = [arguments objectAtIndex:i];
		[arg setAddress:nextAddress];
		nextAddress += [arg length];
	}
	
	EXPStatementBlockElement *block = [function block];
	NSArray *auxiliaries = [block auxiliaries];
	int localStorageSize = 0;
	for(i=0; i<[auxiliaries count]; i++) {
		EXPAuxiliaryElement *aux = [auxiliaries objectAtIndex:i];
		[aux setAddress:nextAddress];
		int length = [aux length];
		nextAddress += length;
		localStorageSize += length;
	}
	
	if (localStorageSize>0) {
		[machine putOpcode:LEAS mode:STACK + INTEGER];
		[machine putUInt:localStorageSize];
	}

	[self compileBlock:block machine:machine error:error];
	[machine putOpcode:RTS mode:0];
	
	return success;
}

- (BOOL) isInitialiser:(id)exp
{
	BOOL isInitialiser;

	if ([exp isKindOfClass:[EXPAssignmentOp class]]) {
		EXPElement *left = [exp left];
		isInitialiser = [[left elementType] isEqualToString:@"parameter"];
	} else if ([exp isKindOfClass:[EXPCompoundElement class]]) {
		EXPBlockElement *block = [(EXPCompoundElement *)exp block];
		isInitialiser = ![block hasStatements];
	} else {
		NSLog(@"??????");
	}
	return isInitialiser;
}

- (void) setOpcodeList:(id)opcodeList
{
	[opcodeList retain];
	[_opcodeList release];
	_opcodeList = opcodeList;
}

- (id) opcodeList
{
	return _opcodeList;
}

// machine & error should be instance variables...
- (BOOL) generate:(EXPBlockElement *)block machine:(EXPModel *)machine error:(EXPError *)error
{
	BOOL success = YES;
	int nextAddress = 0;
	int i;
	BOOL echo = [machine echo];
	
	NSNumber *delayTrapCode = [[NSNumber alloc] initWithInt:[machine registerTrapCall:@selector(delayTrap:machine:) forObject:machine]];
	NSNumber *delay1TrapCode = [[NSNumber alloc] initWithInt:[machine registerTrapCall:@selector(delayTrap1:machine:) forObject:machine]];
	NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:delayTrapCode, @"delayTrapCode", delay1TrapCode, @"delay1TrapCode", nil];
	[self setOpcodeList:dict];
	[dict release];
	[delay1TrapCode release];
	[delayTrapCode release];

	if (echo) printf("\n");

	NSArray *dimensions = [block dimensions];
	[machine setDimensionStartAddress:nextAddress];
	for(i=0; i<[dimensions count]; i++) {
		EXPDimensionElement *dim = [dimensions objectAtIndex:i];
		[dim setAddress:nextAddress];
		if (echo) printf("%08x  %s\n", nextAddress, [[dim name] UTF8String]);
		nextAddress += [EXPDimensionElement storageSize];
	}

	NSArray *parameters = [block parameters];
	[machine setParameterStartAddress:nextAddress];
	int nConstants = 0;
	for(i=0; i<[parameters count]; i++) {
		EXPParameterElement *param = [parameters objectAtIndex:i];
		[param setAddress:nextAddress];
		if (echo) printf("%08x  %s\n", nextAddress, [[param name] UTF8String]);
		nextAddress += [param length];
		nConstants += [param numberOfElements];
	}
	[machine setNConstants:nConstants];
	
	NSMutableArray *auxiliaryNames = [[NSMutableArray alloc] init];
	NSArray *auxiliaries = [block auxiliaries];
	int nAuxiliaries = 0;
	[machine setAuxiliaryStartAddress:nextAddress];
	for(i=0; i<[auxiliaries count]; i++) {
		EXPAuxiliaryElement *aux = [auxiliaries objectAtIndex:i];
		[aux setAddress:nextAddress];
		if (echo) printf("%08x  %s\n", nextAddress, [[aux name] UTF8String]);
		int nElements = [aux numberOfElements];
		int j;
		if (nElements>1) {
			for(j=0; j<nElements; j++) {
				NSString *header = [[NSString alloc] initWithFormat:@"%@[%d]", [aux name], j];
				[auxiliaryNames addObject:header];
				[header release];
			}
		} else {
			[auxiliaryNames addObject:[aux name]];
		}
		nextAddress += [aux length];
		nAuxiliaries += nElements;
	}
	[machine setNAuxiliaries:nAuxiliaries];
	[machine setAuxiliaryNames:auxiliaryNames];
	[auxiliaryNames release];

	NSArray *variables = [block variables];
	[machine setVariableStartAddress:nextAddress];
	int nVariables = 0;
	int nextDelayNumber = 0;
	NSMutableArray *delayVariables = [[NSMutableArray alloc] init];
	NSMutableArray *variableNames = [[NSMutableArray alloc] init];

	for(i=0; i<[variables count]; i++) {
		EXPStateElement *var = [variables objectAtIndex:i];
		[var setAddress:nextAddress];
		if (echo) printf("%08x  %s\n", nextAddress, [[var name] UTF8String]);
		int nElements = [var numberOfElements];
		nextAddress += [var length];
		nVariables += nElements;
		if (nElements>1) {
			int j;
			for(j=0; j<nElements; j++) {
				NSString *header = [[NSString alloc] initWithFormat:@"%@[%d]", [var name], j];
				[variableNames addObject:header];
				[header release];
			}
		} else {
			[auxiliaryNames addObject:[var name]];
		}

//		if (echo) printf("%s\n", [[var elementType] UTF8String]);
		if ([[var elementType] isEqualToString:@"variable"]) {
			BOOL isDelayed = [var isDelayVariable];
			if (isDelayed) {
				[var setDelayNumber:nextDelayNumber];
				nextDelayNumber += [var numberOfElements];
				[delayVariables addObject:var];
			}

		}
		
	}
	[machine setVariableNames:variableNames];
	[variableNames release];
	
	EXPSymbolTable *symbolTable = [block symbolTable];
	NSMutableDictionary *names = [block names];
	EXPParameterElement *tStart = [symbolTable symbolForName:[names objectForKey:@"tstart"]];
	[machine setStartTimeAddress:[tStart address]];
	EXPParameterElement *tStop = [symbolTable symbolForName:[names objectForKey:@"tstop"]];
	[machine setStopTimeAddress:[tStop address]];
	EXPAuxiliaryElement *time = [symbolTable symbolForName:[names objectForKey:@"t"]];
	[machine setTimeAddress:[time address]];

	[machine setNVariables:nVariables];
	[machine setNHistory:nextDelayNumber];
	[machine setDelays:delayVariables];
	[delayVariables release];
	
	[machine setGradientStartAddress:nextAddress];
	for(i=0; i<[variables count]; i++) {
		EXPStateElement *var = [variables objectAtIndex:i];
		if ([[var elementType] isEqualToString:@"variable"]) {
			[var setGradientAddress:nextAddress];
			if (echo) printf("%08x  %s'\n", nextAddress, [[var name] UTF8String]);
			nextAddress += [var length];
			}
	}
	if (echo) printf("\n");
	
	[machine setTablesStartAddress:nextAddress];
	NSArray *tables = [block tables];
	for(i=0; i<[tables count]; i++)
	{
		EXPTableElement *table = [tables objectAtIndex:i];
		[table setAddress:nextAddress];
		nextAddress += [table length];
	}

	NSArray *switches = [block switches];
	int nSwitches = [switches count];
	[machine setNSwitches:nSwitches];
	[machine setSwitchesStartAddress:nextAddress];

	for(i=0; i<nSwitches; i++) {
		EXPSwitchElement *switcher = [switches objectAtIndex:i];
		[switcher setAddress:nextAddress];
		nextAddress += [EXPSwitchElement storageSize];
	}

	[machine setDataTop:nextAddress];

	[machine setPtr:0];
	NSArray *functions = [block functions];
	for(i=0; i<[functions count]; i++) {
		EXPUserFunctionElement *function = [functions objectAtIndex:i];
		success = [self generateFunctionDefinition:function machine:machine error:error];
	}
	
	NSArray *outputters = [block outputters];
	
	if (echo) printf("Compiling parameter initialisations\n");
	int parameterInitialisationAddress = [machine ptr];
	[machine setParameterInitialisationAddress:parameterInitialisationAddress];
	NSArray *initialisers = [block initialisers];
	for(i=0; i<[initialisers count]; i++) {
		EXPAssignmentOp *exp = [initialisers objectAtIndex:i];
/*		BOOL isInitialiser;
		if ([exp isKindOfClass:[EXPAssignmentOp class]]) {
			EXPElement *left = [exp left];
			isInitialiser = [[left elementType] isEqualToString:@"parameter"];
		} else if ([exp isKindOfClass:[EXPCompoundElement class]]) {
			EXPBlockElement *block = [(EXPCompoundElement *)exp block];
			isInitialiser = ![block hasStatements];
		} else {
			NSLog(@"??????");
		} */
		if ([self isInitialiser:exp]) {
//			success = [self compileAssignmentOp:exp machine:machine error:error];
			success = [self compileStatement:exp machine:machine error:error];
			if (!success) {
//				printf("Could not compile parameter initialisation for %s\n", [[left name] UTF8String]);
			}
		}
	}
	[machine putOpcode:HLT mode:0];

//	Compile non-parameter initialisations (variables, auxiliaries)
	if (echo) printf("Compiling non-parameter initialisations\n");
	int variableInitialisationAddress = [machine ptr];
	[machine setVariableInitialisationAddress:variableInitialisationAddress];

	for(i=0; i<[tables count]; i++) {
		EXPTableElement *tab = [tables objectAtIndex:i];
		[self compileAllocateTable:tab machine:machine error:error];
	}

	for(i=0; i<[initialisers count]; i++) {
		EXPAssignmentOp *exp = [initialisers objectAtIndex:i];
//		EXPElement *left = [exp left];
//		if ([[left elementType] isEqualToString:@"parameter"]) {
/*		if (![self isInitialiser:exp]) {
			continue;
		} else if ([[left elementType] isEqualToString:@"table"]) {
			success = [self compileTable:(EXPTableElement *)[exp left] machine:machine error:error];
		} else {
			success = [self compileAssignmentOp:exp machine:machine error:error];
		} */
		if (![self isInitialiser:exp]) {
			success = [self compileStatement:exp machine:machine error:error];
		}
		if (!success) {
//			printf("Could not compile non-parameter initialisation for %s\n", [[left name] UTF8String]);
		}
	}
	[machine putOpcode:HLT mode:0];
	
	int variableComputationAddress = [machine ptr];
	[machine setVariableComputationAddress:variableComputationAddress];
	NSArray *statements = [block statements];
	if (echo) printf("Compiling  statements\n");
//	NSLog(@"statements = %@", statements);
	for(i=0; i<[statements count]; i++) {
		EXPExpression *statement = [statements objectAtIndex:i];
		success = [self compileStatement:statement machine:machine error:error];
		if (!success) {
			printf("Could not compile assignment\n");
		}
	}
	[machine putOpcode:HLT mode:0];

	if (echo) printf("Compiling switches\n");
	if (nSwitches>0) {
		int switchesExecutionAddress = [machine ptr];
		[machine setSwitchesExecutionAddress:switchesExecutionAddress];
		for(i=0; i<nSwitches; i++) {
			EXPSwitchElement *switcher = [switches objectAtIndex:i];
			success = [self compileSwitchHeader:switcher machine:machine error:error];
			if (!success) {
				printf("Could not compile switch header\n");
			}
			[machine putOpcode:STA mode:FLTPOINT+GLOBAL];
			[machine putUInt:[switcher address]];
		}
		[machine putOpcode:HLT mode:0];
	
		[machine setMapExecutionAddress:[machine ptr]];
		[machine putOpcode:SWT mode:0];
		int branchTableAddress = [machine ptr];
		for(i=0; i<nSwitches; i++) {
			[machine putUInt:0];
		}
	
		unsigned int *breakAddress = (unsigned int *)calloc(nSwitches, sizeof(unsigned int));
		for(i=0; i<nSwitches; i++) {
			EXPSwitchElement *switcher = [switches objectAtIndex:i];
			[machine putUInt:[machine ptr] at:(branchTableAddress + i*M)];
			success = [self compileSwitchStatements:switcher machine:machine error:error];
			if (!success) {
				printf("Could not compile switch body\n");
			}
			[machine putOpcode:BRA mode:LONG];
			breakAddress[i] = [machine ptr];
			[machine putUInt:0];
		}
	
		unsigned int here = [machine ptr];
		for(i=0; i<nSwitches; i++) {
			[machine putUInt:here at:breakAddress[i]];
		}
		free(breakAddress);
	
		[machine putOpcode:HLT mode:0];
	}

	if (echo) printf("Compiling outputters\n");
	int outputtersExecutionAddress = [machine ptr];
	[machine setOutputtersExecutionAddress:outputtersExecutionAddress];
	for(i=0; i<[outputters count]; i++) {
		EXPOutputElement *outputter = [outputters objectAtIndex:i];
		success = [self compileOutputter:outputter machine:machine error:error];
		if (!success) {
			printf("Could not compile outputter\n");
		}
	}
//	[machine putOpcode:HLT mode:0];
	
//	Compile code to deallocate any table functions.
	if (echo) printf("Compiling wash-up code\n");
	[machine setWashupExecutionAddress:[machine ptr]];
	for(i=0; i<[tables count]; i++) {
		EXPTableElement *tab = [tables objectAtIndex:i];
		[self compileReleaseTable:tab machine:machine error:error];
	}
	[machine putOpcode:HLT mode:0];
	
	[machine setCodeTop:[machine ptr]];
	
	[machine setOutputters:outputters];
	
	return success;
//	return NO;
}

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

@end