Hi!
As the review system seems to be down at the moment, here's the
error handling patch for LanguageKit and SmalltalkKit. This patch
changes the AST nodes to report errors using delegation to an
error handler object instead of throwing exceptions.
You can have a look at the applied diff in my branches of the
two kits.
Please have a look at it and tell me what you think! :)
Best regards,
Günther
Index: LanguageKit/LKErrorHandler.h
===================================================================
--- LanguageKit/LKErrorHandler.h (Revision 0)
+++ LanguageKit/LKErrorHandler.h (Revision 0)
@@ -0,0 +1,34 @@
+
+#import <Foundation/Foundation.h>
+#import "LKToken.h"
+
+/**
+ * A delegate class able to handle parsing errors and warnings.
+ *
+ * A warning is a recoverable error (compilation can still be done),
+ * while an error in an unrecoverable error (compilation can't
+ * succeed any more).
+ *
+ * If you just want to parse source code, it may be useful to
+ * subclass the default error handler to ignore some errors.
+ */
+...@interface LKErrorHandler : NSObject
+
+/**
+ * Handles a warning consisting of:
+ * - a warning name / identifier
+ * - a start token
+ */
+- (void) handleWarningName: (NSString*)warningName
+ startToken: (LKToken*)startToken;
+
+/**
+ * Handles an error consisting of:
+ * - an error name / identifier
+ * - a start token
+ */
+- (void) handleErrorName: (NSString*)errorName
+ startToken: (LKToken*)startToken;
+
+...@end
+
Index: LanguageKit/LKAST.h
===================================================================
--- LanguageKit/LKAST.h (Revision 4165)
+++ LanguageKit/LKAST.h (Arbeitskopie)
@@ -1,7 +1,9 @@
#import "LKSymbolTable.h"
#import "LKCodeGen.h"
+#import "LKToken.h"
@class LKCompilationUnit;
+
/**
* Root class for AST nodes. Every node in the Smalltalk abstract syntax tree
* inherits from this. It stores the parent, allowing navigation up the tree,
@@ -72,6 +74,16 @@
* Returns YES for AST nodes with no code generation
*/
- (BOOL) isComment;
+/**
+ * Signals a warning to the error handler registered with the module.
+ */
+- (void) handleWarningName: (NSString*)warningName
+ startToken: (LKToken*)startToken;
+/**
+ * Signals an error to the error handler registered with the module.
+ */
+- (void) handleErrorName: (NSString*)errorName
+ startToken: (LKToken*)startToken;
@end
#define SAFECAST(type, obj) ([obj isKindOfClass:[type class]] ? (type*)obj : ([NSException raise:@"InvalidCast" format:@"Can not cast %@ to %s", obj, #type], (type*)nil))
Index: LanguageKit/LKAST.m
===================================================================
--- LanguageKit/LKAST.m (Revision 4165)
+++ LanguageKit/LKAST.m (Arbeitskopie)
@@ -1,5 +1,6 @@
#import "LKAST.h"
#import "LKDeclRef.h"
+#import "LKModule.h"
Class DeclRefClass;
Class ModuleClass;
@@ -89,13 +90,15 @@
{
case 0:
{
- [NSException raise:@"InvalidSymbol"
- format:@"Unrecognised symbol %@", aChild];
+ [self handleErrorName:@"InvalidSymbol"
+ startToken: nil];
+ break;
}
case global:
{
- [NSException raise:@"InvalidLValue"
- format:@"Global symbol %@ is not a valid l-value", aChild];
+ [self handleErrorName:@"InvalidLValue"
+ startToken: nil];
+ break;
}
default:
{
@@ -105,16 +108,16 @@
}
else
{
- [NSException raise:@"InvalidLValue"
- format:@"Result of an expression (%@) may not be used as an l-value", aChild];
+ // Result of an expression (%@) may not be used as an l-value
+ [self handleErrorName:@"LValueMayNotBeExpression" startToken: nil];
}
}
- (void*) compileWith:(id<LKCodeGenerator>)aGenerator
{
NSLog(@"Compiling...");
- [NSException raise:@"NotImplementedException"
- format:@"Code generation not yet implemented for %@",
- [self class]];
+ // Code generation must be provided in subclasses.
+ // XXX: Replace with call to -subclassResponsibility:?
+ [self handleErrorName: @"CodeGenNotImplemented" startToken: nil];
return NULL;
}
- (LKSymbolTable*) symbols
@@ -125,4 +128,20 @@
{
return NO;
}
+
+- (void) handleWarningName: (NSString*)warningName
+ startToken: (LKToken*)startToken
+{
+ // forwards to the module's error handler
+ [[[self module] errorHandler] handleWarningName: warningName
+ startToken: startToken];
+}
+
+- (void) handleErrorName: (NSString*)errorName
+ startToken: (LKToken*)startToken
+{
+ // forwards to the module's error handler
+ [[[self module] errorHandler] handleErrorName: errorName
+ startToken: startToken];
+}
@end
Index: LanguageKit/LKErrorHandler.m
===================================================================
--- LanguageKit/LKErrorHandler.m (Revision 0)
+++ LanguageKit/LKErrorHandler.m (Revision 0)
@@ -0,0 +1,20 @@
+
+#import "LKErrorHandler.h"
+
+...@implementation LKErrorHandler
+
+- (void) handleWarningName: (NSString*)warningName
+ startToken: (LKToken*)startToken
+{
+ NSLog(@"WARNING -- %@ at token %@", warningName, startToken);
+}
+
+- (void) handleErrorName: (NSString*)errorName
+ startToken: (LKToken*)startToken
+{
+ NSLog(@"ERROR -- %@ at token %@", errorName, startToken);
+}
+
+...@end
+
+
Index: LanguageKit/LKDeclRef.m
===================================================================
--- LanguageKit/LKDeclRef.m (Revision 4165)
+++ LanguageKit/LKDeclRef.m (Arbeitskopie)
@@ -22,16 +22,17 @@
switch ([symbols scopeOfSymbol:symbol])
{
case invalid:
- [NSException raise:@"InvalidSymbol"
- format:@"Unrecognised symbol %@", symbol];
+ // Unrecognized symbol
+ [self handleErrorName: @"InvalidSymbol" startToken: nil];
+ break;
case external:
{
LKExternalSymbolScope s =
[(LKBlockSymbolTable*)symbols scopeOfExternal:symbol];
if (nil == s.scope)
{
- [NSException raise:@"InvalidSymbol"
- format:@"Unrecognised symbol %@", symbol];
+ // Unrecognized symbol
+ [self handleErrorName: @"InvalidSymbol" startToken: nil];
}
}
default:
Index: LanguageKit/GNUmakefile
===================================================================
--- LanguageKit/GNUmakefile (Revision 4165)
+++ LanguageKit/GNUmakefile (Arbeitskopie)
@@ -40,6 +40,7 @@
LKSymbolRef.m\
LKSymbolTable.m\
LKToken.m\
+ LKErrorHandler.m\
LKVariableDecl.m
${FRAMEWORK_NAME}_HEADER_FILES = \
@@ -65,6 +66,7 @@
LKSymbolRef.h\
LKSymbolTable.h\
LKToken.h\
+ LKErrorHandler.h\
LKVariableDecl.h\
LanguageKit.h
Index: LanguageKit/LKCompiler.h
===================================================================
--- LanguageKit/LKCompiler.h (Revision 4165)
+++ LanguageKit/LKCompiler.h (Arbeitskopie)
@@ -1,6 +1,8 @@
#import <Foundation/NSObject.h>
@class NSBundle;
@class NSString;
+...@class LKErrorHandler;
+...@protocol LKCodeGenerator;
/**
* Abstract class implementing a dynamic language compiler. Concrete
@@ -15,8 +17,6 @@
+ (void) setDebugMode:(BOOL)aFlag;
/**
* Compile and load the specified string.
- *
- * Must be implemented by subclasses.
*/
+ (BOOL) compileString:(NSString*)s;
/**
@@ -25,6 +25,16 @@
*/
+ (BOOL) compileString:(NSString*) source output:(NSString*)bitcode;
/**
+ * Compiles the string using a specific generator and a specific
+ * error handler.
+ *
+ * If you want to change the compiling behaviour, this is the method
+ * to change.
+ */
++ (BOOL) compileString:(NSString*)s
+ withGenerator:(id<LKCodeGenerator>)cg
+ errorHandler:(LKErrorHandler*)anErrorHandler;
+/**
* Load a framework with the specified name.
*/
+ (BOOL) loadFramework:(NSString*)framework;
Index: LanguageKit/LKCompiler.m
===================================================================
--- LanguageKit/LKCompiler.m (Revision 4165)
+++ LanguageKit/LKCompiler.m (Arbeitskopie)
@@ -1,10 +1,12 @@
#import <EtoileFoundation/EtoileFoundation.h>
#import "LKCompiler.h"
#import "LKAST.h"
+#import "LKErrorHandler.h"
// Semi-formal protocol for parsers.
@protocol LKParser
- (LKAST*) parseString:(NSString*)aProgram;
+- (LKAST*) parseString:(NSString*)aProgram errorHandler: (LKErrorHandler*) handler;
@end
static NSMutableDictionary *compilersByExtension;
@@ -24,15 +26,15 @@
FOREACH(classes, nextClass, Class)
{
[compilersByLanguage setObject:nextClass
- forKey:[nextClass languageName]];
+ forKey:[nextClass languageName]];
[compilersByExtension setObject:nextClass
- forKey:[nextClass fileExtension]];
+ forKey:[nextClass fileExtension]];
}
}
+ (id) alloc
{
[NSException raise:@"InstantiationError"
- format:@"LKCompiler instances are invalid"];
+ format:@"LKCompiler instances are invalid"];
return nil;
}
+ (void) setDebugMode:(BOOL)aFlag
@@ -41,10 +43,18 @@
}
+ (BOOL) compileString:(NSString*)s withGenerator:(id<LKCodeGenerator>)cg
{
+ return [self compileString: s
+ withGenerator: cg
+ errorHandler: [LKErrorHandler new]];
+}
++ (BOOL) compileString:(NSString*)s
+ withGenerator:(id<LKCodeGenerator>)cg
+ errorHandler:(LKErrorHandler*)anErrorHandler
+{
id p = [[[[self parser] alloc] init] autorelease];
LKAST *ast;
NS_DURING
- ast = [p parseString: s];
+ ast = [p parseString: s errorHandler: anErrorHandler];
NS_HANDLER
NSDictionary *e = [localException userInfo];
if ([[localException name] isEqualToString:@"ParseError"])
Index: LanguageKit/LKSubclass.m
===================================================================
--- LanguageKit/LKSubclass.m (Revision 4165)
+++ LanguageKit/LKSubclass.m (Arbeitskopie)
@@ -37,9 +37,8 @@
[[LKObjectSymbolTable symbolTableForNewClassNamed:superclass] copy];
if (symbols == nil)
{
- [NSException raise:@"SemanticError"
- format:@"Unable to find superclass %@ for %@",
- superclass, classname];
+ [self handleErrorName: @"SuperclassNotPresent"
+ startToken: nil];
}
}
else
@@ -48,8 +47,8 @@
}
if (Nil != NSClassFromString(classname))
{
- [NSException raise:@"SemanticError"
- format:@"Can not create new class %@ - a class of this name already exists.", classname];
+ [self handleErrorName: @"ClassAlreadyExists"
+ startToken: nil];
}
//Construct symbol table.
FOREACH(ivars, ivar, NSString*)
@@ -60,7 +59,9 @@
{
[(LKObjectSymbolTable*)symbols addClassVariable:cvar];
}
- [(LKObjectSymbolTable*)symbols registerNewClass:classname];
+
+ [(LKObjectSymbolTable*)symbols registerNewClass:classname];
+
FOREACH(methods, method, LKAST*)
{
[method setParent:self];
@@ -104,7 +105,6 @@
const char *cvarNames[[cvars count] + 1];
const char *cvarTypes[[cvars count] + 1];
- int cvarOffsets[[cvars count] + 1];
for (int i=0; i<[cvars count]; i++)
{
cvarNames[i] = [[cvars objectAtIndex: i] UTF8String];
Index: LanguageKit/LKModule.h
===================================================================
--- LanguageKit/LKModule.h (Revision 4165)
+++ LanguageKit/LKModule.h (Arbeitskopie)
@@ -1,5 +1,7 @@
#import "LKAST.h"
+...@class LKErrorHandler;
+
/**
* AST node representing a module - a set of classes and categories compiled
* together.
@@ -13,6 +15,8 @@
NSMutableDictionary * pragmas;
/** Manually-specified method types. */
NSMutableDictionary *typeOverrides;
+ /** Error handler */
+ LKErrorHandler* errorHandler;
}
/**
* Add compile-time pragmas.
@@ -30,4 +34,12 @@
* Returns the type that should be used for a given selector.
*/
- (const char*) typeForMethod:(NSString*)methodName;
+/**
+ * Sets the receiver's error handler.
+ */
+- (void) setErrorHandler: (LKErrorHandler*)errorHandler;
+/**
+ * Returns the error handler.
+ */
+- (LKErrorHandler*) errorHandler;
@end
Index: LanguageKit/LKAssignExpr.m
===================================================================
--- LanguageKit/LKAssignExpr.m (Revision 4165)
+++ LanguageKit/LKAssignExpr.m (Arbeitskopie)
@@ -58,8 +58,8 @@
// TODO: Move this to -check
if ([[symbols typeOfSymbol:target->symbol] characterAtIndex:0] != '@')
{
- [NSException raise:@"InvalidAssignmentException"
- format:@"Can not yet generate code for assignment"];
+ // Can not yet generate code for assignment
+ [self handleErrorName: @"InvalidAssignment" startToken: nil];
}
// Assign
[aGenerator storeValue:rval
@@ -92,8 +92,8 @@
// TODO: Move this to -check
if ([[scope.scope typeOfSymbol:target->symbol] characterAtIndex:0] != '@')
{
- [NSException raise:@"InvalidAssignmentException"
- format:@"Can not yet generate code for assignment"];
+ // Can not yet generate code for assignment
+ [self handleErrorName: @"InvalidAssignment" startToken: nil];
}
// Assign
[aGenerator storeValue:rval
Index: LanguageKit/LKModule.m
===================================================================
--- LanguageKit/LKModule.m (Revision 4165)
+++ LanguageKit/LKModule.m (Arbeitskopie)
@@ -1,4 +1,5 @@
#import "LKModule.h"
+#import "LKErrorHandler.h"
static NSMutableDictionary *SelectorConflicts = nil;
@@ -97,6 +98,8 @@
// If it's a conflicted type, pick the default and log a warning
if (nil != (type = [SelectorConflicts objectForKey:methodName]))
{
+ // XXX: How is the error handler supposed to figure out the assumed type?
+ [self handleWarningName: @"PolymorphicSelector" startToken: nil];
NSLog(@"Warning: Selector '%@' is polymorphic. Assuming %@",
methodName, type);
return [type UTF8String];
@@ -149,4 +152,13 @@
object:nil];
return NULL;
}
+- (void) setErrorHandler: (LKErrorHandler*)anErrorHandler
+{
+ ASSIGN(errorHandler, anErrorHandler);
+}
+- (LKErrorHandler*) errorHandler
+{
+ return errorHandler;
+}
@end
+
Index: SmalltalkKit/SmalltalkParser.h
===================================================================
--- SmalltalkKit/SmalltalkParser.h (Revision 4165)
+++ SmalltalkKit/SmalltalkParser.h (Arbeitskopie)
@@ -1,21 +1,33 @@
#import <EtoileFoundation/EtoileFoundation.h>
@class LKAST;
+...@class LKErrorHandler;
+
/**
* Smalltalk parser class. This class implements a tokeniser and calls a
* parser created using the Lemon parser generator.
*/
@interface SmalltalkParser : NSObject {
LKAST *delegate;
+ LKErrorHandler* defaultErrorHandler;
}
/**
+ * See -[SmalltalkParser parseString:errorHandler:]
+ */
+- (LKAST*) parseString:(NSString*)s;
+/**
* Parse the specified Smalltalk string and return an abstract syntax tree for
* the program.
*/
-- (LKAST*) parseString:(NSString*)s;
+- (LKAST*) parseString:(NSString*)s
+ errorHandler:(LKErrorHandler*) errorHandler;
/**
* Used by the Lemon implementation to feed the generated AST back so the
* Parser can return it.
*/
- (void) setDelegate:(LKAST*)ast;
+/**
+ * Sets the error handler that's used when parseString: is called.
+ */
+- (void) setDefaultErrorHandler:(LKErrorHandler*) anErrorHandler;
@end
Index: SmalltalkKit/SmalltalkParser.m
===================================================================
--- SmalltalkKit/SmalltalkParser.m (Revision 4165)
+++ SmalltalkKit/SmalltalkParser.m (Arbeitskopie)
@@ -2,6 +2,8 @@
#import <EtoileFoundation/EtoileFoundation.h>
#import <LanguageKit/LKToken.h>
#import <LanguageKit/LKAST.h>
+#import <LanguageKit/LKModule.h>
+#import <LanguageKit/LKToken.h>
#include <ctype.h>
#include "smalltalk.h"
@@ -30,6 +32,12 @@
- (LKAST*) parseString:(NSString*)s
{
+ return [self parseString: s
+ errorHandler: defaultErrorHandler];
+}
+
+- (LKAST*) parseString:(NSString*)s errorHandler: (LKErrorHandler*) errorHandler
+{
unsigned sLength = [s length];
/* Cache some IMPs of methods we call a lot */
SEL charSel = @selector(characterAtIndex:);
@@ -169,6 +177,7 @@
NS_ENDHANDLER
SmalltalkParse(parser, 0, nil, self);
SmalltalkParseFree(parser, free);
+ [(LKCompilationUnit*)delegate setErrorHandler: errorHandler];
[delegate check];
return delegate;
}
@@ -177,4 +186,10 @@
{
ASSIGN(delegate, ast);
}
+
+- (void) setDefaultErrorHandler: (LKErrorHandler*) anErrorHandler
+{
+ ASSIGN(defaultErrorHandler, anErrorHandler);
+}
+
@end
_______________________________________________
Etoile-dev mailing list
[email protected]
https://mail.gna.org/listinfo/etoile-dev