/** STCompiler.m Bytecode compiler. Generates STExecutableCode from source code. Copyright (c) 2002 Free Software Foundation This file is part of the StepTalk project. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #import "STCompiler.h" #import "STMessage.h" #import "STLiterals.h" #import "STBytecodes.h" #import "STCompiledScript.h" #import "STCompiledCode.h" #import "STCompiledMethod.h" #import "STCompilerUtils.h" #import "STSourceReader.h" #import "Externs.h" #import #import #import #import #import #import #import #import #import #import extern int STCparse(void *context); @interface STCompiler(STCompilerPrivate) - (void)compile; - (void)initializeContext; - (unsigned)tempVariableIndex:(NSString *)varName; - (unsigned)externalVariableIndex:(NSString *)varName; - (void) initializeCompilationContext; - (NSDictionary *)exceptionInfo; - (unsigned)addSelectorLiteral:(NSString*)selector; - (unsigned)addLiteral:literal; - (void)compileMethod:(STCMethod *)method; - (STCompiledCode *) compileStatements:(STCStatements *)statements; - (void)compileStatements:(STCStatements *)statements blockFlag:(BOOL)blockFlag; - (void)compilePrimary:(STCPrimary *)primary; - (void)compileExpression:(STCExpression *)expr; - (void)emitPushReceiverVariable:(unsigned)index; - (void)emitPushSelf; - (void)emitPopAndStoreReceiverVariable:(unsigned)index; - (void)emitReturn; - (void)emitReturnFromBlock; - (void)emitPushTemporary:(unsigned)index; - (void)emitPushLiteral:(unsigned)index; - (void)emitPushVariable:(unsigned)index; - (void)emitPopAndStoreTemporary:(unsigned)index; - (void)emitPopAndStoreVariable:(unsigned)index ; - (void)emitPopStack; - (void)emitSendSelector:(unsigned)index argCount:(unsigned)argCount; - (void)emitBlockCopy; - (void)emitDuplicateStackTop; - (void)emitJump:(short)offset; - (void)emitLongJump:(short)offset; - (void)emitPushNil; - (void)emitPushTrue; - (void)emitPushFalse; - (void)fixupLongJumpAt:(unsigned)index with:(short)offset; - (unsigned)currentBytecode; @end #define MAX(a,b) (((a)>(b))?(a):(b)) @implementation STCompiler - init { [super init]; arrayLiteralClass = [NSMutableArray class]; stringLiteralClass = [NSMutableString class]; /* bytesLiteralClass = [NSMutableData class]; */ intNumberLiteralClass = [NSNumber class]; realNumberLiteralClass = [NSNumber class]; symbolLiteralClass = [STSelector class]; characterLiteralClass = [NSString class]; return self; } - (void)dealloc { [super dealloc]; } /* --------------------------------------------------------------------------- * Compilation * --------------------------------------------------------------------------- */ - (void)setEnvironment:(STEnvironment *)env { ASSIGN(environment,env); } - (STCompiledScript *)compileString:(NSString *)aString { STCompiledScript *result; NSString *exceptionFmt = @"Syntax error at line %i near '%@', " @"reason: %@."; int parsRetval = 0; NSDebugLLog(@"STCompiler", @"Compile string", aString); if(!environment) { [NSException raise:STCompilerGenericException format:@"Compilation environment is not initialized"]; return nil; } reader = [[STSourceReader alloc] initWithString:aString]; receiverVars = [[NSMutableArray alloc] init]; STParserContextInit(&context,self,reader); NS_DURING { // extern int STCdebug; // STCdebug = 1; parsRetval = STCparse(&context); } NS_HANDLER { if ([[localException name] isEqualToString: STCompilerSyntaxException]) { RELEASE(reader); reader = nil; [NSException raise:STCompilerSyntaxException format:exceptionFmt, [reader currentLine], [reader tokenString], [localException reason]]; } [localException raise]; } NS_ENDHANDLER RELEASE(receiverVars); RELEASE(reader); result = script; script = nil; return AUTORELEASE(result); } - (void)compileMethod:(STCMethod *)method { STCompiledMethod *compiledMethod; STCompiledCode *code; STMessage *messagePattern; if(!script) { NSDebugLLog(@"STCompiler", @"Creating script with %i variables",[receiverVars count]); script = [[STCompiledScript alloc] initWithVariableCount: [receiverVars count]]; } /* FIXME: unite STCMessage and STMessage */ messagePattern = (STMessage *)[method messagePattern]; if(!messagePattern) { messagePattern = [STMessage messageWithSelector:@"_unnamed_method" arguments:nil]; } NSDebugLLog(@"STCompiler", @"Compile method %@", [messagePattern selector]); tempVars = [NSMutableArray arrayWithArray:[messagePattern arguments]]; code = [self compileStatements:[method statements]]; compiledMethod = [STCompiledMethod methodWithCode:AUTORELEASE(code) messagePattern:messagePattern]; [script addMethod:compiledMethod]; } - (STCompiledCode *)compileStatements:(STCStatements *)statements { STCompiledCode *compiledCode; int count; int i; /* FIXME: create another class */ [self initializeCompilationContext]; NSDebugLLog(@"STCompiler", @"compiling statements"); tempsSize = tempsCount = [tempVars count]; [self compileStatements:statements blockFlag:NO]; NSDebugLLog(@"STCompiler", @" temporaries %i stack %i", tempsSize,stackSize); #ifdef DEBUG count = [literals count]; NSDebugLLog(@"STCompiler", @" literals count %i", count); for(i=0;i 0; index--) { [self emitPopAndStoreTemporary:tempsSave + index - 1]; } } array = [statements expressions]; if(array) { enumerator = [array objectEnumerator]; while((expr = [enumerator nextObject])) { [self compileExpression:expr]; } } expr = [statements returnExpression]; if(expr) { [self compileExpression:expr]; } if(blockFlag) { [blockInfo setStackSize:stackSize]; AUTORELEASE(blockInfo); stackSize = stackSizeSave; stackPos = stackPosSave; [self emitReturnFromBlock]; } else { [self emitReturn]; } /* fixup jump (if block) */ if(blockFlag) { [self fixupLongJumpAt:jumpIP with:[self currentBytecode] - jumpIP]; } /* cleanup unneeded temp variables */ [tempVars removeObjectsInRange:NSMakeRange(tempsSave, [tempVars count]-tempsSave)]; tempsCount = tempsSave; } - (void)compilePrimary:(STCPrimary *)primary { id object = [primary object]; int index; NSDebugLLog(@"STCompiler-misc",@" compile primary"); switch([primary type]) { case STCVariablePrimaryType: if([object isEqualToString:@"YES"] || [object isEqualToString:@"true"]) { [self emitPushTrue]; } else if([object isEqualToString:@"NO"] || [object isEqualToString:@"false"]) { [self emitPushFalse]; } else if([object isEqualToString:@"nil"]) { [self emitPushNil]; } else { index = [self tempVariableIndex:object]; if(index != NSNotFound) { [self emitPushTemporary:index]; break; } else if( (index = [self receiverVariableIndex:object])!= NSNotFound) { [self emitPushReceiverVariable:index]; break; } else if( [object isEqual:@"self"] ) { [self emitPushSelf]; break; } index = [self externalVariableIndex:object]; [self emitPushVariable:index]; } break; case STCLiteralPrimaryType: index = [self addLiteral:object]; [self emitPushLiteral:index]; break; case STCBlockPrimaryType: [self compileStatements:object blockFlag:YES]; break; case STCExpressionPrimaryType: [self compileExpression:object]; break; default: [NSException raise:STCompilerInconsistencyException format:@"Unknown primary type %i", [primary type]]; break; } } - (void)compileMessage:(STCMessage *)message { NSEnumerator *enumerator; NSArray *args; id obj; int index; args = [message arguments]; if(args && ([args count]>0)) { enumerator = [args objectEnumerator]; while((obj = [enumerator nextObject])) { if([obj isKindOfClass:[STCPrimary class]]) [self compilePrimary:obj]; else [self compileExpression:obj]; } } index = [self addSelectorLiteral:[message selector]]; [self emitSendSelector:index argCount:[args count]]; } - (void)compileExpression:(STCExpression *)expr { NSEnumerator *enumerator; NSArray *cascade; NSString *varName; NSArray *array; unsigned count; unsigned index,i; id obj; NSDebugLLog(@"STCompiler-misc",@" compile expression"); if([expr isPrimary]) { [self compilePrimary:[expr object]]; } else /* message expression */ { obj = [expr target]; /* target */ if([obj isKindOfClass:[STCPrimary class]]) [self compilePrimary:obj]; else [self compileExpression:obj]; cascade = [expr cascade]; if(cascade) { count = [cascade count]; for(i=0;i0) { for(i = 0; i