/* -*- mode: objc; coding: utf-8 -*- */
/* Étoilisp/Mulklisp, a Common Lisp subset for the Étoilé runtime.
* Copyright (C) 2008 Matthias Andreas Benkard.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#import "MLKInterpretedClosure.h"
#import "MLKCons.h"
#import "MLKDynamicContext.h"
#import "MLKEnvironment.h"
#import "MLKFuncallable.h"
#import "MLKInterpreter.h"
#import "MLKLexicalContext.h"
#import "MLKLexicalEnvironment.h"
#import "MLKPackage.h"
#import "MLKReader.h"
#import "MLKRoot.h"
#import "MLKSymbol.h"
#import "NSObject-MLKPrinting.h"
#import "runtime-compatibility.h"
#import "util.h"
#import
#import
#import
#import
#include
static MLKPackage *cl;
static MLKPackage *sys;
static MLKSymbol *IF;
static MLKSymbol *IN_PACKAGE;
static MLKSymbol *DECLARE;
static MLKSymbol *PROGN;
static MLKSymbol *TAGBODY;
static MLKSymbol *GO;
static MLKSymbol *CATCH;
static MLKSymbol *THROW;
static MLKSymbol *LAMBDA;
static MLKSymbol *LET;
static MLKSymbol *APPLY;
static MLKSymbol *FUNCALL;
static MLKSymbol *EVAL;
static MLKSymbol *QUOTE;
static MLKSymbol *SETQ;
static MLKSymbol *SETF;
static MLKSymbol *SET;
static MLKSymbol *_FSET;
static MLKSymbol *PROGV;
static MLKSymbol *UNWIND_PROTECT;
static MLKSymbol *VALUES;
static MLKSymbol *_DEFMACRO;
static MLKSymbol *_LAMBDA;
@implementation MLKInterpreter
+(void) initialize
{
cl = [MLKPackage findPackage:@"COMMON-LISP"];
sys = [MLKPackage findPackage:@"TOILET-SYSTEM"];
IF = [cl intern:@"IF"];
IN_PACKAGE = [cl intern:@"IN-PACKAGE"];
DECLARE = [cl intern:@"DECLARE"];
PROGN = [cl intern:@"PROGN"];
TAGBODY = [cl intern:@"TAGBODY"];
GO = [cl intern:@"GO"];
CATCH = [cl intern:@"CATCH"];
THROW = [cl intern:@"THROW"];
LAMBDA = [cl intern:@"LAMBDA"];
LET = [cl intern:@"LET"];
APPLY = [cl intern:@"APPLY"];
EVAL = [cl intern:@"EVAL"];
QUOTE = [cl intern:@"QUOTE"];
SETQ = [cl intern:@"SETQ"];
SETF = [cl intern:@"SETF"];
SET = [cl intern:@"SET"];
_FSET = [sys intern:@"%FSET"];
PROGV = [cl intern:@"PROGV"];
VALUES = [cl intern:@"VALUES"];
UNWIND_PROTECT = [cl intern:@"UNWIND-PROTECT"];
_DEFMACRO = [sys intern:@"%DEFMACRO"];
_LAMBDA = [sys intern:@"%LAMBDA"];
}
+(NSArray*) eval:(id)program
inLexicalContext:(MLKLexicalContext *)context
withEnvironment:(MLKLexicalEnvironment *)lexenv
{
MLKDynamicContext *dynamicContext = [MLKDynamicContext currentContext];
//NSLog (@"eval: %@", [program descriptionForLisp]);
if (!program || [program isKindOfClass:[MLKSymbol class]])
{
//NSLog (@"Processing symbol.");
if ([context symbolNamesSymbolMacro:program])
{
id macrofun = [context macroForSymbol:program];
id expansion = [macrofun applyToArray:
[NSArray arrayWithObjects:
program, context, nil]];
return [self eval:expansion
inLexicalContext:context
withEnvironment:lexenv];
}
else if ([context variableIsLexical:program])
{
//NSLog (@"Processing lexical variable %@.", [program descriptionForLisp]);
//NSLog (@"Lexical environment: %@.", lexenv);
//NSLog (@"Lexical variable value: %@.", [lexenv valueForSymbol:program]);
return [NSArray arrayWithObject:nullify([lexenv valueForSymbol:program])];
}
else
{
//NSLog (@"Processing special variable %@.", [program descriptionForLisp]);
//NSLog (@"Dynamic context: %@.", dynamicContext);
//NSLog (@"Special variable value: %@.", [dynamicContext valueForSymbol:program]);
return [NSArray arrayWithObject:nullify([dynamicContext valueForSymbol:program])];
}
}
else if (![program isKindOfClass:[MLKCons class]])
{
// Everything that is not a list or a symbol evaluates to itself.
return [NSArray arrayWithObject:nullify(program)];
}
else
{
id car = [program car];
if ([car isKindOfClass:[MLKSymbol class]] || !car)
{
if (car == APPLY)
{
MLKCons *rest = denullify([[self eval:[[[program cdr] cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0]);
id function = denullify([[self eval:[[program cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0]);
if ([function isKindOfClass:[MLKSymbol class]])
function = [lexenv functionForSymbol:function];
return [function applyToArray:(rest
? (id)[rest array]
: (id)[NSArray array])];
}
else if (car == CATCH)
{
id catchTag;
NSArray *values;
NS_DURING
{
catchTag = denullify([[self eval:[[program cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0]);
values = [self eval:[MLKCons cons:PROGN with:[[program cdr] cdr]]
inLexicalContext:context
withEnvironment:lexenv];
NS_VALUERETURN (values, NSArray *);
}
NS_HANDLER
{
if ([[localException name] isEqualToString:@"MLKThrow"])
{
id thrownTag = [[localException userInfo]
objectForKey:@"THROWN TAG"];
if (thrownTag == catchTag)
return [[localException userInfo]
objectForKey:@"THROWN OBJECTS"];
else
[localException raise];
}
else
[localException raise];
}
NS_ENDHANDLER;
return nil;
}
else if (car == _DEFMACRO)
{
// No real lambda lists here. This SYS::%DEFMACRO is
// really as low-level as it gets.
id name = [[program cdr] car];
id lambdaListAndBody = [[program cdr] cdr];
id function;
function = denullify([[self eval:[MLKCons cons:_LAMBDA with:lambdaListAndBody]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0]);
[context addMacro:function forSymbol:name];
return [NSArray arrayWithObject:nullify(name)];
}
else if (car == EVAL)
{
return [self eval:denullify([[self eval:[program cdr]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0])
inLexicalContext:[MLKLexicalContext globalContext]
withEnvironment:[MLKLexicalEnvironment
globalEnvironment]];
}
else if (car == IF)
{
id condition = [[program cdr] car];
id consequent = [[[program cdr] cdr] car];
// Incidentally works for the two-clause case:
id alternative = [[[[program cdr] cdr] cdr] car];
NSArray *values = [self eval:condition
inLexicalContext:context
withEnvironment:lexenv];
if ([values objectAtIndex:0] == [NSNull null])
return [self eval:alternative
inLexicalContext:context
withEnvironment:lexenv];
else
return [self eval:consequent
inLexicalContext:context
withEnvironment:lexenv];
}
else if (car == IN_PACKAGE)
{
id cadr = [[program cdr] car];
id package = [MLKPackage findPackage:stringify(cadr)];
[[MLKDynamicContext currentContext]
setValue:package
forSymbol:[[MLKPackage findPackage:@"COMMON-LISP"]
intern:@"*PACKAGE*"]];
return [NSArray arrayWithObject:nullify(package)];
}
else if (car == _LAMBDA)
{
// A bare-bones LAMBDA without a real lambda list. What
// would be a lambda list in a real LAMBDA form must be a
// symbol here.
id lambdaList = [[program cdr] car];
id body = [[program cdr] cdr];
MLKInterpretedClosure *closure;
closure = AUTORELEASE ([[MLKInterpretedClosure alloc]
initWithBodyForms:body
lambdaListName:lambdaList
context:context
environment:lexenv]);
return [NSArray arrayWithObject:nullify(closure)];
}
else if (car == LET)
{
id declarations;
id clauses;
id body;
NSArray *result;
MLKLexicalContext *ctx;
MLKLexicalEnvironment *env;
MLKDynamicContext *dynctx;
body = [[program cdr] cdr];
if ([[body car] isKindOfClass:[MLKCons class]]
&& [[body car] car] == DECLARE)
{
declarations = [[body car] cdr];
body = [body cdr];
}
else
{
declarations = nil;
}
env = AUTORELEASE ([[MLKLexicalEnvironment alloc]
initWithParent:lexenv
variables:nil
functions:nil]);
ctx = AUTORELEASE ([[MLKLexicalContext alloc]
initWithParent:context
variables:nil
functions:nil
goTags:nil
macros:nil
compilerMacros:nil
symbolMacros:nil
declarations:declarations]);
dynctx = [[MLKDynamicContext alloc]
initWithParent:dynamicContext
variables:nil
handlers:nil
restarts:nil
catchTags:nil
activeHandlerEnvironment:nil];
clauses = [[program cdr] car];
while (clauses)
{
id clause = [clauses car];
id variable, value;
if (!clause || [clause isKindOfClass:[MLKSymbol class]])
{
variable = clause;
value = nil;
}
else if ([clause cdr] == nil)
{
variable = [clause car];
value = nil;
}
else
{
variable = [clause car];
value = denullify([[self eval:[[clause cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0]);
}
[ctx addVariable:variable];
if ([ctx variableIsLexical:variable])
{
[env addValue:value forSymbol:variable];
}
else
{
[dynctx addValue:value forSymbol:variable];
}
clauses = [clauses cdr];
}
[dynctx pushContext];
NS_DURING
{
result = [self eval:[MLKCons cons:PROGN with:body]
inLexicalContext:ctx
withEnvironment:env];
}
NS_HANDLER
{
[MLKDynamicContext popContext];
[localException raise];
}
NS_ENDHANDLER;
[MLKDynamicContext popContext];
RELEASE (dynctx);
return result;
}
else if (car == PROGN)
{
id result = nil;
id rest = program;
while ((rest = [rest cdr]))
{
result = [self eval:[rest car]
inLexicalContext:context
withEnvironment:lexenv];
}
return result;
}
else if (car == QUOTE)
{
return [NSArray arrayWithObject:nullify([[program cdr] car])];
}
else if (car == SETQ)
{
id symbol = [[program cdr] car];
id value = [[self eval:[[[program cdr] cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0];
id rest = [[[program cdr] cdr] cdr];
if (![program cdr])
return [NSArray arrayWithObject:[NSNull null]];
if ([context symbolNamesSymbolMacro:symbol])
{
id macrofun = [context macroForSymbol:program];
id expansion = [macrofun applyToArray:
[NSArray arrayWithObjects:
program, context, nil]];
return [self eval:
[MLKCons cons:SETF
with:
[MLKCons cons:expansion
with:
[[program cdr] cdr]]]
inLexicalContext:context
withEnvironment:lexenv];
}
if ([context variableIsLexical:symbol])
[lexenv setValue:value forSymbol:symbol];
else if ([dynamicContext bindingForSymbol:symbol])
[dynamicContext setValue:value forSymbol:symbol];
else
// FIXME: Maybe print a warning.
[[MLKDynamicContext globalContext] addValue:value
forSymbol:symbol];
if (rest)
return [self eval:[MLKCons cons:SETQ with:rest]
inLexicalContext:context
withEnvironment:lexenv];
else
return [NSArray arrayWithObject:value];
}
else if (car == SET)
{
id symbol = [[self eval:[[program cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0];
id value = [[self eval:[[[program cdr] cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0];
if ([dynamicContext bindingForSymbol:symbol])
[dynamicContext setValue:value forSymbol:symbol];
else
[[MLKDynamicContext globalContext] addValue:value
forSymbol:symbol];
return [NSArray arrayWithObject:symbol];
}
else if (car == _FSET)
{
// Like SET, but for the function cell.
id symbol = [[self eval:[[program cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0];
id value = [[self eval:[[[program cdr] cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0];
[[MLKLexicalContext globalContext] addFunction:symbol];
[[MLKLexicalEnvironment globalEnvironment] addFunction:value
forSymbol:symbol];
return [NSArray arrayWithObject:symbol];
}
else if (car == TAGBODY)
{
//FIXME: ...
}
else if (car == THROW)
{
id catchTag;
NSArray *values;
NSDictionary *userInfo;
catchTag = denullify([[self eval:[[program cdr] car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0]);
values = [self eval:[[[program cdr] cdr] car]
inLexicalContext:context
withEnvironment:lexenv];
userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
catchTag, @"THROWN TAG",
values, @"THROWN OBJECTS", nil];
[[NSException exceptionWithName:@"MLKThrow"
reason:[NSString stringWithFormat:
@"THROW without a corresponding CATCH: tag %@, values %@.",
[catchTag descriptionForLisp],
[values descriptionForLisp]]
userInfo:userInfo] raise];
return nil;
}
else if (car == UNWIND_PROTECT)
{
NSArray *results;
NS_DURING
{
results = [self eval:[[program cdr] car]
inLexicalContext:context
withEnvironment:lexenv];
}
NS_HANDLER
{
[self eval:[MLKCons cons:PROGN with:[[program cdr] cdr]]
inLexicalContext:context
withEnvironment:lexenv];
[localException raise];
}
NS_ENDHANDLER;
[self eval:[MLKCons cons:PROGN with:[[program cdr] cdr]]
inLexicalContext:context
withEnvironment:lexenv];
return results;
}
else if (car == VALUES)
{
id results = [NSMutableArray array];
id rest = program;
while ((rest = [rest cdr]))
{
[results addObject:
[[self eval:[rest car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0]];
}
return results;
}
else
{
if ([context symbolNamesFunction:car])
{
id function = [lexenv functionForSymbol:car];
MLKCons *rest = [program cdr];
NSMutableArray *args = [NSMutableArray array];
while (rest)
{
id result = [[self eval:[rest car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0];
[args addObject:result];
rest = [rest cdr];
}
return [function applyToArray:args];
}
else if ([context symbolNamesMacro:car])
{
id macrofun = [context macroForSymbol:car];
id expansion = denullify([[macrofun
applyToArray:
[NSArray arrayWithObjects:
program, context, nil]]
objectAtIndex:0]);
return [self eval:expansion
inLexicalContext:context
withEnvironment:lexenv];
}
else
{
NSMutableArray *args = [NSMutableArray array];
MLKCons *rest = [program cdr];
NSArray *results;
while (rest)
{
id result = [[self eval:[rest car]
inLexicalContext:context
withEnvironment:lexenv]
objectAtIndex:0];
[args addObject:result];
rest = [rest cdr];
}
results = [MLKRoot dispatch:car withArguments:args];
if (results)
{
return results;
}
else
{
[NSException raise:@"MLKNoSuchOperatorException"
format:@"%@ does not name a known operator.",
[car descriptionForLisp]];
return nil;
}
}
}
}
else if ([car isKindOfClass:[MLKCons class]] && [car car] == LAMBDA)
{
return [self eval:[MLKCons cons:FUNCALL with:program]
inLexicalContext:context
withEnvironment:lexenv];
}
else
{
[NSException raise:@"MLKInvalidExpressionException"
format:@"%@ is not a valid operator name.",
[car descriptionForLisp]];
return nil;
}
}
}
+(BOOL) load:(MLKStream *)stream verbose:(BOOL)verbose print:(BOOL)print
{
id eofValue = [[NSObject alloc] init];
while (YES)
{
id result;
//NSLog (@"; LOAD: Reding a form.");
id code = [MLKReader readFromStream:stream
eofError:NO
eofValue:eofValue
recursive:NO
preserveWhitespace:NO];
//NSLog (@"; LOAD: Reading finished.");
NSString *formdesc;
//NSLog (@"%@", code);
//NSLog (@"%@", [code descriptionForLisp]);
//NSLog (@"%@", stream);
//NSLog (@"...");
if (code == eofValue)
break;
if ([code isKindOfClass:[MLKCons class]] && [code cdr])
formdesc = [NSString stringWithFormat:@"(%@ %@ ...)",
[[code car] descriptionForLisp],
[[[code cdr] car] descriptionForLisp]];
else
formdesc = [code descriptionForLisp];
fprintf (stderr, "; LOAD: %s\n",
[formdesc UTF8String]);
result = [MLKInterpreter
eval:code
inLexicalContext:[MLKLexicalContext globalContext]
withEnvironment:[MLKLexicalEnvironment globalEnvironment]];
//NSLog (@"; LOAD: Top-level form evaluated.");
if (print)
{
//FIXME
//NSLog (@"; LOAD: Fnord. Primary value: %@",
// [[result objectAtIndex:0] descriptionForLisp]);
}
}
//NSLog (@"; LOAD: END");
return YES;
}
@end