diff options
| -rw-r--r-- | GNUmakefile | 25 | ||||
| -rw-r--r-- | MLKBackquoteReader.h | 27 | ||||
| -rw-r--r-- | MLKBackquoteReader.m | 62 | ||||
| -rw-r--r-- | MLKCommaReader.h | 27 | ||||
| -rw-r--r-- | MLKCommaReader.m | 73 | ||||
| -rw-r--r-- | MLKCons.m | 16 | ||||
| -rw-r--r-- | MLKDynamicContext.m | 12 | ||||
| -rw-r--r-- | MLKInterpreter.h | 5 | ||||
| -rw-r--r-- | MLKInterpreter.m | 364 | ||||
| -rw-r--r-- | MLKPackage.m | 4 | ||||
| -rw-r--r-- | MLKParenReader.m | 34 | ||||
| -rw-r--r-- | MLKReader.h | 7 | ||||
| -rw-r--r-- | MLKReader.m | 25 | ||||
| -rw-r--r-- | MLKRoot.m | 29 | ||||
| -rw-r--r-- | cond.lisp | 29 | ||||
| -rw-r--r-- | destructuring-bind.lisp | 114 | ||||
| -rw-r--r-- | init.lisp | 2 | ||||
| -rw-r--r-- | util.lisp | 195 | 
18 files changed, 940 insertions, 110 deletions
diff --git a/GNUmakefile b/GNUmakefile index 4af0e66..22398e9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -27,18 +27,19 @@ BUNDLE_NAME = Test  ADDITIONAL_OBJCFLAGS = -Wall -ToiletKit_OBJC_FILES = MLKCharacter.m MLKCons.m MLKBinding.m			\ -                       MLKDoubleFloat.m MLKDynamicContext.m			\ -                       MLKEnvironment.m MLKFloat.m MLKInteger.m			\ -                       MLKInterpretedClosure.m MLKInterpreter.m			\ -                       MLKLinkedList.m MLKLexicalContext.m			\ -                       MLKLexicalEnvironment.m MLKLispValue.m			\ -                       MLKNumber.m MLKPackage.m MLKParenReader.m		\ -                       MLKQuoteReader.m MLKRatio.m MLKReader.m			\ -                       MLKReadtable.m MLKReaderError.m MLKRoot.m		\ -                       MLKSemicolonReader.m MLKSingleFloat.m MLKStream.m	\ -                       MLKStringInputStream.m MLKStringOutputStream.m		\ -                       MLKStringReader.m MLKSymbol.m MLKThrowException.m	\ +ToiletKit_OBJC_FILES = MLKCharacter.m MLKCons.m MLKBackquoteReader.m	\ +                       MLKBinding.m MLKCommaReader.m MLKDoubleFloat.m	\ +                       MLKDynamicContext.m MLKEnvironment.m MLKFloat.m	\ +                       MLKInteger.m MLKInterpretedClosure.m		\ +                       MLKInterpreter.m MLKLinkedList.m			\ +                       MLKLexicalContext.m MLKLexicalEnvironment.m	\ +                       MLKLispValue.m MLKNumber.m MLKPackage.m		\ +                       MLKParenReader.m MLKQuoteReader.m MLKRatio.m	\ +                       MLKReader.m MLKReadtable.m MLKReaderError.m	\ +                       MLKRoot.m MLKSemicolonReader.m MLKSingleFloat.m	\ +                       MLKStream.m MLKStringInputStream.m		\ +                       MLKStringOutputStream.m MLKStringReader.m	\ +                       MLKSymbol.m MLKThrowException.m			\                         NSObject-MLKPrinting.m NSString-MLKPrinting.m  ToiletKit_LDFLAGS = -lgmp  #LIBRARIES_DEPEND_UPON diff --git a/MLKBackquoteReader.h b/MLKBackquoteReader.h new file mode 100644 index 0000000..1c35bfa --- /dev/null +++ b/MLKBackquoteReader.h @@ -0,0 +1,27 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#import "MLKFuncallable.h" +#import "MLKLispValue.h" + +#import <Foundation/NSArray.h> + + +@interface MLKBackquoteReader : MLKLispValue <MLKFuncallable> +-(NSArray *) applyToArray:(NSArray *)arguments; +@end diff --git a/MLKBackquoteReader.m b/MLKBackquoteReader.m new file mode 100644 index 0000000..47989ab --- /dev/null +++ b/MLKBackquoteReader.m @@ -0,0 +1,62 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#import "MLKBackquoteReader.h" + +#import "MLKCons.h" +#import "MLKDynamicContext.h" +#import "MLKReader.h" +#import "MLKReadtable.h" +#import "MLKPackage.h" +#import "MLKStream.h" +#import "runtime-compatibility.h" +#import "util.h" + + +@implementation MLKBackquoteReader +-(NSArray *) applyToArray:(NSArray *)arguments +{ +  MLKStream *stream; +  MLKReadtable *readtable; +  MLKPackage *cl, *sys; +  unichar ch; +  id quoted_form; + +  cl = [MLKPackage findPackage:@"COMMON-LISP"]; +  sys = [MLKPackage findPackage:@"TOILET-SYSTEM"]; + +  stream = [arguments objectAtIndex:0]; +  readtable = [[MLKDynamicContext currentContext] +                valueForSymbol:[cl intern:@"*READTABLE*"]]; + +  while ([readtable isWhitespaceCharacter:(ch = [stream readChar])]); + +  [stream unreadChar:ch]; + +  quoted_form = [MLKReader readFromStream:stream +                           eofError:YES +                           eofValue:nil +                           recursive:YES +                           preserveWhitespace:NO]; + +  return [NSArray arrayWithObject: +                    [MLKCons cons:[sys intern:@"QUASIQUOTE"] +                             with:[MLKCons cons:nullify(quoted_form) +                                           with:nil]]]; +} +@end diff --git a/MLKCommaReader.h b/MLKCommaReader.h new file mode 100644 index 0000000..5f5d11e --- /dev/null +++ b/MLKCommaReader.h @@ -0,0 +1,27 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#import "MLKFuncallable.h" +#import "MLKLispValue.h" + +#import <Foundation/NSArray.h> + + +@interface MLKCommaReader : MLKLispValue <MLKFuncallable> +-(NSArray *) applyToArray:(NSArray *)arguments; +@end diff --git a/MLKCommaReader.m b/MLKCommaReader.m new file mode 100644 index 0000000..3a0115b --- /dev/null +++ b/MLKCommaReader.m @@ -0,0 +1,73 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#import "MLKCommaReader.h" + +#import "MLKCons.h" +#import "MLKDynamicContext.h" +#import "MLKReader.h" +#import "MLKReadtable.h" +#import "MLKPackage.h" +#import "MLKStream.h" +#import "runtime-compatibility.h" +#import "util.h" + + +@implementation MLKCommaReader +-(NSArray *) applyToArray:(NSArray *)arguments +{ +  MLKStream *stream; +  MLKReadtable *readtable; +  MLKPackage *cl, *sys; +  MLKSymbol *marker; +  unichar ch; +  id quoted_form; + +  cl = [MLKPackage findPackage:@"COMMON-LISP"]; +  sys = [MLKPackage findPackage:@"TOILET-SYSTEM"]; + +  stream = [arguments objectAtIndex:0]; +  readtable = [[MLKDynamicContext currentContext] +                valueForSymbol:[cl intern:@"*READTABLE*"]]; + +  if ((ch = [stream readChar]) == '@') +    { +      marker = [sys intern:@"UNQUOTE-SPLICING"]; +    } +  else +    { +      marker = [sys intern:@"UNQUOTE"]; +      [stream unreadChar:ch]; +    } + +  while ([readtable isWhitespaceCharacter:(ch = [stream readChar])]); + +  [stream unreadChar:ch]; + +  quoted_form = [MLKReader readFromStream:stream +                           eofError:YES +                           eofValue:nil +                           recursive:YES +                           preserveWhitespace:NO]; + +  return [NSArray arrayWithObject: +                    [MLKCons cons:marker +                             with:[MLKCons cons:nullify(quoted_form) +                                           with:nil]]]; +} +@end @@ -113,10 +113,18 @@  -(NSString *)descriptionForLisp  { -  if ([_cdr isKindOfClass:[MLKCons class]] -      && _car == [[MLKPackage findPackage:@"COMMON-LISP"] intern:@"QUOTE"]) -    return [NSString stringWithFormat:@"'%@", [_cdr bareDescriptionForLisp]]; -  else +  if ([_cdr isKindOfClass:[MLKCons class]]) +    { +      if (_car == [[MLKPackage findPackage:@"COMMON-LISP"] intern:@"QUOTE"]) +        return [NSString stringWithFormat:@"'%@", [_cdr bareDescriptionForLisp]]; +      else if (_car == [[MLKPackage findPackage:@"TOILET-SYSTEM"] intern:@"QUASIQUOTE"]) +        return [NSString stringWithFormat:@"`%@", [_cdr bareDescriptionForLisp]]; +      else if (_car == [[MLKPackage findPackage:@"TOILET-SYSTEM"] intern:@"UNQUOTE"]) +        return [NSString stringWithFormat:@",%@", [_cdr bareDescriptionForLisp]]; +      else if (_car == [[MLKPackage findPackage:@"TOILET-SYSTEM"] intern:@"UNQUOTE-SPLICING"]) +        return [NSString stringWithFormat:@",@%@", [_cdr bareDescriptionForLisp]]; +    } +      return [NSString stringWithFormat:@"(%@)", [self bareDescriptionForLisp]];  } diff --git a/MLKDynamicContext.m b/MLKDynamicContext.m index 593f4a7..1b8725b 100644 --- a/MLKDynamicContext.m +++ b/MLKDynamicContext.m @@ -24,6 +24,8 @@  #import <Foundation/NSString.h>  #import <Foundation/NSThread.h> +#import "MLKBackquoteReader.h" +#import "MLKCommaReader.h"  #import "MLKCons.h"  #import "MLKDynamicContext.h"  #import "MLKEnvironment.h" @@ -147,8 +149,6 @@ static MLKDynamicContext *global_context;    [readtable setSyntaxType:MULTI_ESCAPE forCharacter:'|'];    //  [readtable setSyntaxType:NONTERMINATING_MACRO forCharacter:'#']; -  //  [readtable setSyntaxType:TERMINATING_MACRO forCharacter:'`']; -  //  [readtable setSyntaxType:TERMINATING_MACRO forCharacter:','];    [readtable setSyntaxType:SINGLE_ESCAPE forCharacter:'\\']; @@ -164,6 +164,14 @@ static MLKDynamicContext *global_context;    [readtable setSyntaxType:TERMINATING_MACRO forCharacter:'\''];    [readtable setMacroFunction:AUTORELEASE([[MLKQuoteReader alloc] init])               forCharacter:'\'']; +   +  [readtable setSyntaxType:TERMINATING_MACRO forCharacter:'`']; +  [readtable setMacroFunction:AUTORELEASE([[MLKBackquoteReader alloc] init]) +             forCharacter:'`']; +   +  [readtable setSyntaxType:TERMINATING_MACRO forCharacter:',']; +  [readtable setMacroFunction:AUTORELEASE([[MLKCommaReader alloc] init]) +             forCharacter:','];    [readtable setSyntaxType:TERMINATING_MACRO forCharacter:';'];    [readtable setMacroFunction:AUTORELEASE([[MLKSemicolonReader alloc] init]) diff --git a/MLKInterpreter.h b/MLKInterpreter.h index 788dfe1..8ff119f 100644 --- a/MLKInterpreter.h +++ b/MLKInterpreter.h @@ -31,5 +31,10 @@              inLexicalContext:(MLKLexicalContext *)context              withEnvironment:(MLKLexicalEnvironment *)lexenv; ++(NSArray*) eval:(id)program +            inLexicalContext:(MLKLexicalContext *)context +            withEnvironment:(MLKLexicalEnvironment *)lexenv +            expandOnly:(BOOL)expandOnly; +    +(BOOL) load:(MLKStream *)stream verbose:(BOOL)verbose print:(BOOL)print;  @end diff --git a/MLKInterpreter.m b/MLKInterpreter.m index c2d7ba4..3d55f33 100644 --- a/MLKInterpreter.m +++ b/MLKInterpreter.m @@ -102,9 +102,25 @@ static MLKSymbol *_LAMBDA;              inLexicalContext:(MLKLexicalContext *)context              withEnvironment:(MLKLexicalEnvironment *)lexenv  { +  return (NSArray *)[self eval:program +                          inLexicalContext:context +                          withEnvironment:lexenv +                          expandOnly:NO]; +} + + +#define RETURN_VALUE(thing)                     \ +  { return [NSArray arrayWithObject:nullify(thing)]; } + + ++(NSArray*) eval:(id)program +            inLexicalContext:(MLKLexicalContext *)context +            withEnvironment:(MLKLexicalEnvironment *)lexenv +            expandOnly:(BOOL)expandOnly +{    MLKDynamicContext *dynamicContext = [MLKDynamicContext currentContext]; -  //NSLog (@"eval: %@", [program descriptionForLisp]); +  //  NSLog (@"eval: %@", [program descriptionForLisp]);    if (!program || [program isKindOfClass:[MLKSymbol class]])      { @@ -117,27 +133,34 @@ static MLKSymbol *_LAMBDA;                                                  program, context, nil]];            return [self eval:expansion                         inLexicalContext:context -                       withEnvironment:lexenv]; +                       withEnvironment:lexenv +                       expandOnly:expandOnly];          }        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])]; +          if (expandOnly) +            RETURN_VALUE (program); + +          RETURN_VALUE ([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])]; +          if (expandOnly) +            RETURN_VALUE (program); + +          RETURN_VALUE ([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)]; +      RETURN_VALUE (program);      }    else      { @@ -149,14 +172,22 @@ static MLKSymbol *_LAMBDA;              {                MLKCons *rest = denullify([[self eval:[[[program cdr] cdr] car]                                                 inLexicalContext:context -                                               withEnvironment:lexenv] +                                               withEnvironment:lexenv +                                               expandOnly:expandOnly]                                            objectAtIndex:0]);                id function = denullify([[self eval:[[program cdr] car]                                               inLexicalContext:context -                                             withEnvironment:lexenv] +                                             withEnvironment:lexenv +                                             expandOnly:expandOnly]                                          objectAtIndex:0]); +              if (expandOnly) +                RETURN_VALUE ([MLKCons cons:APPLY +                                       with:[MLKCons cons:function +                                                     with:[MLKCons cons:rest +                                                                   with:nil]]]); +                if ([function isKindOfClass:[MLKSymbol class]])                  function = [lexenv functionForSymbol:function]; @@ -174,21 +205,33 @@ static MLKSymbol *_LAMBDA;                  {                    catchTag = [[self eval:[[program cdr] car]                                      inLexicalContext:context -                                    withEnvironment:lexenv] +                                    withEnvironment:lexenv +                                    expandOnly:expandOnly]                                 objectAtIndex:0]; - -                  newctx = [[MLKDynamicContext alloc] -                             initWithParent:dynamicContext -                             variables:nil -                             handlers:nil -                             restarts:nil -                             catchTags:[NSSet setWithObject:catchTag] -                             activeHandlerEnvironment:nil]; -                  [newctx pushContext]; +                   +                  if (!expandOnly) +                    { +                      newctx = [[MLKDynamicContext alloc] +                                 initWithParent:dynamicContext +                                 variables:nil +                                 handlers:nil +                                 restarts:nil +                                 catchTags:[NSSet setWithObject:catchTag] +                                 activeHandlerEnvironment:nil]; +                      [newctx pushContext]; +                    }                    values = [self eval:[MLKCons cons:PROGN with:[[program cdr] cdr]]                                   inLexicalContext:context -                                 withEnvironment:lexenv]; +                                 withEnvironment:lexenv +                                 expandOnly:expandOnly]; + +                  if (expandOnly) +                    NS_VALUERETURN ([NSArray arrayWithObject: +                                               [MLKCons cons:CATCH +                                                        with:[MLKCons cons:catchTag +                                                                      with:values]]], +                                    NSArray *);                    [MLKDynamicContext popContext];                    RELEASE (newctx); @@ -227,21 +270,49 @@ static MLKSymbol *_LAMBDA;                id <MLKFuncallable> function; +              if (expandOnly) +                { +                  id lambdaList = [lambdaListAndBody car]; +                  id body = [lambdaListAndBody cdr]; +                  id body_expansion = +                    denullify([[self eval:[MLKCons cons:PROGN with:body] +                                     inLexicalContext:context +                                     withEnvironment:lexenv +                                     expandOnly:expandOnly] +                                objectAtIndex:0]); +                  RETURN_VALUE ([MLKCons +                                  cons:_DEFMACRO +                                  with:[MLKCons +                                         cons:name +                                         with:[MLKCons +                                                cons:lambdaList +                                                with:[MLKCons +                                                       cons:body_expansion +                                                       with:nil]]]]); +                } +                function = denullify([[self eval:[MLKCons cons:_LAMBDA with:lambdaListAndBody]                                            inLexicalContext:context -                                          withEnvironment:lexenv] +                                          withEnvironment:lexenv +                                          expandOnly:expandOnly]                                       objectAtIndex:0]);                [context addMacro:function forSymbol:name]; -              return [NSArray arrayWithObject:nullify(name)]; +              RETURN_VALUE (name);              }            else if (car == EVAL)              { -              return [self eval:denullify([[self eval:[program cdr] -                                                 inLexicalContext:context -                                                 withEnvironment:lexenv] -                                            objectAtIndex:0]) +              NSArray *evaluand = denullify([[self eval:[program cdr] +                                                   inLexicalContext:context +                                                   withEnvironment:lexenv +                                                   expandOnly:expandOnly] +                                              objectAtIndex:0]); + +              if (expandOnly) +                RETURN_VALUE ([MLKCons cons:EVAL with:[MLKCons cons:evaluand with:nil]]); + +              return [self eval:evaluand                             inLexicalContext:[MLKLexicalContext globalContext]                             withEnvironment:[MLKLexicalEnvironment                                               globalEnvironment]]; @@ -253,20 +324,50 @@ static MLKSymbol *_LAMBDA;                // 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]) +              id condition_value = denullify([[self eval:condition +                                                    inLexicalContext:context +                                                    withEnvironment:lexenv +                                                    expandOnly:expandOnly] +                                               objectAtIndex:0]); + +              if (expandOnly) +                { +                  id conseq_expansion = denullify([[self eval:consequent +                                                         inLexicalContext:context +                                                         withEnvironment:lexenv +                                                         expandOnly:expandOnly] +                                                    objectAtIndex:0]); +                  id alt_expansion = denullify([[self eval:alternative +                                                      inLexicalContext:context +                                                      withEnvironment:lexenv +                                                      expandOnly:expandOnly] +                                                 objectAtIndex:0]); +                  RETURN_VALUE ([MLKCons +                                  cons:IF +                                  with:[MLKCons +                                         cons:condition_value +                                         with:[MLKCons +                                                cons:conseq_expansion +                                                with:[MLKCons cons:alt_expansion +                                                              with:nil]]]]); +                } + +              if (!condition_value)                  return [self eval:alternative                               inLexicalContext:context -                             withEnvironment:lexenv]; +                             withEnvironment:lexenv +                             expandOnly:expandOnly];                else                  return [self eval:consequent                               inLexicalContext:context -                             withEnvironment:lexenv];                 +                             withEnvironment:lexenv +                             expandOnly:expandOnly];                              }            else if (car == IN_PACKAGE)              { +              if (expandOnly) +                RETURN_VALUE (program); +                id cadr = [[program cdr] car];                id package = [MLKPackage findPackage:stringify(cadr)]; @@ -275,7 +376,7 @@ static MLKSymbol *_LAMBDA;                  forSymbol:[[MLKPackage findPackage:@"COMMON-LISP"]                              intern:@"*PACKAGE*"]]; -              return [NSArray arrayWithObject:nullify(package)]; +              RETURN_VALUE (package);              }            else if (car == _LAMBDA)              { @@ -286,6 +387,21 @@ static MLKSymbol *_LAMBDA;                id body = [[program cdr] cdr];                MLKInterpretedClosure *closure; +              if (expandOnly) +                { +                  id body_expansion = denullify([[self eval:[MLKCons cons:PROGN +                                                                     with:body] +                                                       inLexicalContext:context +                                                       withEnvironment:lexenv +                                                       expandOnly:expandOnly] +                                                  objectAtIndex:0]); +                  RETURN_VALUE ([MLKCons +                                  cons:_LAMBDA +                                  with:[MLKCons cons:lambdaList +                                                with:[MLKCons cons:body_expansion +                                                              with:nil]]]); +                } +                closure = AUTORELEASE ([[MLKInterpretedClosure alloc]                                         initWithBodyForms:body                                         lambdaListName:lambdaList @@ -315,6 +431,24 @@ static MLKSymbol *_LAMBDA;                    declarations = nil;                  } +              if (expandOnly) +                { +                  id body_expansion = denullify([[self eval:[MLKCons cons:PROGN +                                                                     with:body] +                                                       inLexicalContext:context +                                                       withEnvironment:lexenv +                                                       expandOnly:expandOnly] +                                                  objectAtIndex:0]); +                  RETURN_VALUE ([MLKCons +                                  cons:LET +                                  with:[MLKCons +                                         cons:[[program cdr] car] +                                         with:[MLKCons +                                                cons:declarations +                                                with:[MLKCons cons:body_expansion +                                                              with:nil]]]]); +                } +                env = AUTORELEASE ([[MLKLexicalEnvironment alloc]                                     initWithParent:lexenv                                     variables:nil @@ -400,34 +534,47 @@ static MLKSymbol *_LAMBDA;              {                id result = nil;                id rest = program; +              NSMutableArray *results = [NSMutableArray array];                while ((rest = [rest cdr]))                  {                    result = [self eval:[rest car]                                   inLexicalContext:context -                                 withEnvironment:lexenv]; +                                 withEnvironment:lexenv +                                 expandOnly:expandOnly]; +                  if (expandOnly) +                    [results addObjectsFromArray:result];                  } -              return result; +              if (expandOnly) +                { +                  RETURN_VALUE ([MLKCons cons:PROGN +                                         with:[MLKCons listWithArray:results]]); +                } +              else +                return result;              }            else if (car == QUOTE)              { -              return [NSArray arrayWithObject:nullify([[program cdr] car])]; +              if (expandOnly) +                RETURN_VALUE (program); +              RETURN_VALUE ([[program cdr] car]);              }            else if (car == SETQ)              {                id symbol = [[program cdr] car];                id value = [[self eval:[[[program cdr] cdr] car]                                  inLexicalContext:context -                                withEnvironment:lexenv] +                                withEnvironment:lexenv +                                expandOnly:expandOnly]                             objectAtIndex:0];                id rest = [[[program cdr] cdr] cdr];                if (![program cdr]) -                return [NSArray arrayWithObject:[NSNull null]]; +                RETURN_VALUE (nil);                if ([context symbolNamesSymbolMacro:symbol])                  { -                  id macrofun = [context macroForSymbol:program]; +                  id macrofun = [context symbolMacroForSymbol:symbol];                    id expansion = [macrofun applyToArray:                                               [NSArray arrayWithObjects:                                                          program, context, nil]]; @@ -438,7 +585,25 @@ static MLKSymbol *_LAMBDA;                                                       with:                                                         [[program cdr] cdr]]]                                 inLexicalContext:context -                               withEnvironment:lexenv]; +                               withEnvironment:lexenv +                               expandOnly:expandOnly]; +                } + +              if (expandOnly) +                { +                  RETURN_VALUE ([MLKCons +                                  cons:SETQ +                                  with:[MLKCons +                                         cons:symbol +                                         with:[MLKCons +                                                cons:value +                                                with:denullify([[self eval: +                                                                        [MLKCons cons:SETQ +                                                                                 with:rest] +                                                                      inLexicalContext:context +                                                                      withEnvironment:lexenv +                                                                      expandOnly:expandOnly] +                                                                 objectAtIndex:0])]]]);                  }                if ([context variableIsLexical:symbol]) @@ -456,19 +621,27 @@ static MLKSymbol *_LAMBDA;                               inLexicalContext:context                               withEnvironment:lexenv];                else -                return [NSArray arrayWithObject:value]; +                RETURN_VALUE (value);              }            else if (car == SET)              {                id symbol = [[self eval:[[program cdr] car]                                   inLexicalContext:context -                                 withEnvironment:lexenv] +                                 withEnvironment:lexenv +                                 expandOnly:expandOnly]                             objectAtIndex:0];                id value = [[self eval:[[[program cdr] cdr] car]                                  inLexicalContext:context -                                withEnvironment:lexenv] +                                withEnvironment:lexenv +                                expandOnly:expandOnly]                            objectAtIndex:0]; +              if (expandOnly) +                RETURN_VALUE ([MLKCons cons:SET +                                       with:[MLKCons cons:symbol +                                                     with:[MLKCons cons:value +                                                                   with:nil]]]); +                if ([dynamicContext bindingForSymbol:symbol])                  [dynamicContext setValue:value forSymbol:symbol];                else @@ -482,13 +655,21 @@ static MLKSymbol *_LAMBDA;                // Like SET, but for the function cell.                id symbol = [[self eval:[[program cdr] car]                                   inLexicalContext:context -                                 withEnvironment:lexenv] +                                 withEnvironment:lexenv +                                 expandOnly:expandOnly]                              objectAtIndex:0];                id value = [[self eval:[[[program cdr] cdr] car]                                  inLexicalContext:context -                                withEnvironment:lexenv] +                                withEnvironment:lexenv +                                expandOnly:expandOnly]                             objectAtIndex:0]; +              if (expandOnly) +                RETURN_VALUE ([MLKCons cons:_FSET +                                       with:[MLKCons cons:symbol +                                                     with:[MLKCons cons:value +                                                                   with:nil]]]); +                [[MLKLexicalContext globalContext] addFunction:symbol];                [[MLKLexicalEnvironment globalEnvironment] addFunction:value                                                           forSymbol:symbol]; @@ -507,12 +688,20 @@ static MLKSymbol *_LAMBDA;                catchTag = [[self eval:[[program cdr] car]                                  inLexicalContext:context -                                withEnvironment:lexenv] +                                withEnvironment:lexenv +                                expandOnly:expandOnly]                             objectAtIndex:0];                values = [self eval:[[[program cdr] cdr] car]                               inLexicalContext:context -                             withEnvironment:lexenv]; +                             withEnvironment:lexenv +                             expandOnly:expandOnly]; + +              if (expandOnly) +                RETURN_VALUE ([MLKCons cons:THROW +                                       with:[MLKCons cons:denullify(catchTag) +                                                     with:[MLKCons cons:denullify([values objectAtIndex:0]) +                                                                   with:nil]]]);                userInfo = [NSDictionary dictionaryWithObjectsAndKeys:                                           catchTag, @"THROWN TAG", @@ -541,6 +730,23 @@ static MLKSymbol *_LAMBDA;              {                NSArray *results; +              if (expandOnly) +                { +                  id protectee = [self eval:[[program cdr] car] +                                       inLexicalContext:context +                                       withEnvironment:lexenv +                                       expandOnly:expandOnly]; +                  id protection = [self eval:[MLKCons cons:PROGN +                                                      with:[[program cdr] cdr]] +                                        inLexicalContext:context +                                        withEnvironment:lexenv +                                        expandOnly:expandOnly]; +                  RETURN_VALUE ([MLKCons cons:UNWIND_PROTECT +                                         with:[MLKCons cons:protectee +                                                       with:[MLKCons cons:protection +                                                                     with:nil]]]); +                } +                NS_DURING                  {                    results = [self eval:[[program cdr] car] @@ -551,7 +757,8 @@ static MLKSymbol *_LAMBDA;                  {                    [self eval:[MLKCons cons:PROGN with:[[program cdr] cdr]]                          inLexicalContext:context -                        withEnvironment:lexenv]; +                        withEnvironment:lexenv +                        expandOnly:expandOnly];                    [localException raise];                  } @@ -573,17 +780,21 @@ static MLKSymbol *_LAMBDA;                    [results addObject:                               [[self eval:[rest car]                                      inLexicalContext:context -                                    withEnvironment:lexenv] +                                    withEnvironment:lexenv +                                    expandOnly:expandOnly]                                 objectAtIndex:0]];                  } +              if (expandOnly) +                RETURN_VALUE ([MLKCons cons:VALUES +                                       with:[MLKCons listWithArray:results]]);                return results;              }            else              {                if ([context symbolNamesFunction:car])                  { -                  id function = [lexenv functionForSymbol:car]; +                  id function;                    MLKCons *rest = [program cdr];                    NSMutableArray *args = [NSMutableArray array]; @@ -591,13 +802,23 @@ static MLKSymbol *_LAMBDA;                      {                        id result = [[self eval:[rest car]                                           inLexicalContext:context -                                         withEnvironment:lexenv] +                                         withEnvironment:lexenv +                                         expandOnly:expandOnly]                                      objectAtIndex:0];                        [args addObject:result];                        rest = [rest cdr];                      } -                   -                  return [function applyToArray:args]; + +                  if (expandOnly) +                    { +                      RETURN_VALUE ([MLKCons cons:[program car] +                                             with:[MLKCons listWithArray:args]]); +                    } +                  else +                    { +                      function = [lexenv functionForSymbol:car]; +                      return [function applyToArray:args]; +                    }                  }                else if ([context symbolNamesMacro:car])                  { @@ -610,7 +831,8 @@ static MLKSymbol *_LAMBDA;                    return [self eval:expansion                                 inLexicalContext:context -                               withEnvironment:lexenv]; +                               withEnvironment:lexenv +                               expandOnly:expandOnly];                  }                else                  { @@ -622,12 +844,19 @@ static MLKSymbol *_LAMBDA;                      {                        id result = [[self eval:[rest car]                                           inLexicalContext:context -                                         withEnvironment:lexenv] +                                         withEnvironment:lexenv +                                         expandOnly:expandOnly]                                      objectAtIndex:0];                        [args addObject:result];                        rest = [rest cdr];                      } +                  if (expandOnly) +                    { +                      RETURN_VALUE ([MLKCons cons:[program car] +                                             with:[MLKCons listWithArray:args]]); +                    } +                    results = [MLKRoot dispatch:car withArguments:args];                    if (results) @@ -648,7 +877,8 @@ static MLKSymbol *_LAMBDA;          {            return [self eval:[MLKCons cons:FUNCALL with:program]                         inLexicalContext:context -                       withEnvironment:lexenv]; +                       withEnvironment:lexenv +                       expandOnly:expandOnly];          }        else          { @@ -668,6 +898,7 @@ static MLKSymbol *_LAMBDA;    while (YES)      {        id result; +      id expansion;        //NSLog (@"; LOAD: Reding a form.");        id code = [MLKReader readFromStream:stream                             eofError:NO @@ -692,12 +923,29 @@ static MLKSymbol *_LAMBDA;        else          formdesc = [code descriptionForLisp]; -      fprintf (stderr, "; LOAD: %s\n", -               [formdesc UTF8String]); +      fprintf (stderr, "; COMPILE-MINIMALLY: %s\n", [formdesc UTF8String]); +      expansion = denullify([[MLKInterpreter +                               eval:code +                               inLexicalContext:[MLKLexicalContext +                                                  globalContext] +                               withEnvironment:[MLKLexicalEnvironment +                                                 globalEnvironment] +                               expandOnly:YES] +                              objectAtIndex:0]); + +      if ([code isKindOfClass:[MLKCons class]] && [code cdr]) +        formdesc = [NSString stringWithFormat:@"(%@ %@ ...)", +                               [[expansion car] descriptionForLisp], +                               [[[expansion cdr] car] descriptionForLisp]]; +      else +        formdesc = [expansion descriptionForLisp]; + +      fprintf (stderr, "; LOAD: %s\n", [formdesc UTF8String]);        result = [MLKInterpreter -                 eval:code +                 eval:expansion                   inLexicalContext:[MLKLexicalContext globalContext] -                 withEnvironment:[MLKLexicalEnvironment globalEnvironment]]; +                 withEnvironment:[MLKLexicalEnvironment globalEnvironment] +                 expandOnly:NO];        //NSLog (@"; LOAD: Top-level form evaluated.");        if (print) diff --git a/MLKPackage.m b/MLKPackage.m index cae3416..039596a 100644 --- a/MLKPackage.m +++ b/MLKPackage.m @@ -115,6 +115,10 @@ static NSMutableDictionary *packages = nil;    [sys export:[sys intern:@"MAKE-SYMBOL"]];    [sys export:[sys intern:@"IMPORT"]];    [sys export:[sys intern:@"INTERN"]]; +  [sys export:[sys intern:@"OBJC-CLASS-OF"]]; +  [sys export:[sys intern:@"OBJC-SUBCLASSP"]]; +  [sys export:[sys intern:@"FIND-OBJC-CLASS"]]; +  [sys export:[sys intern:@"NS-LOG"]];    [cl export:[cl intern:@"*BREAK-ON-SIGNALS*"]];    [cl export:[cl intern:@"*COMPILE-FILE-PATHNAME*"]]; diff --git a/MLKParenReader.m b/MLKParenReader.m index 9287ce9..b61a332 100644 --- a/MLKParenReader.m +++ b/MLKParenReader.m @@ -59,6 +59,7 @@ static unichar slurpWhitespaceAndPeek (MLKStream *stream, MLKReadtable *readtabl    while ((nextChar = slurpWhitespaceAndPeek(stream, readtable)) != ')')      {        id item; +      id dotMarker = [[NSObject alloc] init];        // FIXME: What to do about dots?  Maybe add a new        // singleDotAllowed:(BOOL)dotp argument to readFromStream:...? @@ -66,8 +67,35 @@ static unichar slurpWhitespaceAndPeek (MLKStream *stream, MLKReadtable *readtabl                          eofError:YES                          eofValue:nil                          recursive:YES -                        preserveWhitespace:NO]; -       +                        preserveWhitespace:NO +                        singleDotMarker:dotMarker]; + +      if (item == dotMarker) +        { +          id nextItem; + +          RELEASE (dotMarker); + +          nextItem = [MLKReader readFromStream:stream +                                eofError:YES +                                eofValue:nil +                                recursive:YES +                                preserveWhitespace:NO +                                singleDotMarker:nil]; +          [tail setCdr:nextItem]; + +          if ((nextChar = slurpWhitespaceAndPeek (stream, readtable)) == ')') +            { +              [stream readChar]; +              return [NSArray arrayWithObject:cons]; +            } +          else +            { +              [NSException raise:@"MLKReaderError" +                           format:@"Unexpectedly read a single dot."]; +            } +        } +        if (!tail)          {            cons = tail = [MLKCons cons:item with:nil]; @@ -77,6 +105,8 @@ static unichar slurpWhitespaceAndPeek (MLKStream *stream, MLKReadtable *readtabl            [tail setCdr:[MLKCons cons:item with:nil]];            tail = [tail cdr];          } + +      RELEASE (dotMarker);      }    [stream readChar]; diff --git a/MLKReader.h b/MLKReader.h index 9a49ef9..60954f1 100644 --- a/MLKReader.h +++ b/MLKReader.h @@ -26,6 +26,13 @@              eofError:(BOOL)eofError              eofValue:(id)eofValue             recursive:(BOOL)recursive +  preserveWhitespace:(BOOL)preserveWhitespace +     singleDotMarker:(id)dotMarker; + ++(id) readFromStream:(MLKStream *)stream +            eofError:(BOOL)eofError +            eofValue:(id)eofValue +           recursive:(BOOL)recursive    preserveWhitespace:(BOOL)preserveWhitespace;  +(id) readFromString:(NSString *)string; diff --git a/MLKReader.m b/MLKReader.m index 7343c64..7db9670 100644 --- a/MLKReader.m +++ b/MLKReader.m @@ -45,6 +45,21 @@             recursive:(BOOL)recursive    preserveWhitespace:(BOOL)preserveWhitespace  { +  return [self readFromStream:stream +               eofError:eofError +               eofValue:eofValue +               recursive:recursive +               preserveWhitespace:preserveWhitespace +               singleDotMarker:nil]; +} + ++(id) readFromStream:(MLKStream *)stream +            eofError:(BOOL)eofError +            eofValue:(id)eofValue +           recursive:(BOOL)recursive +  preserveWhitespace:(BOOL)preserveWhitespace +     singleDotMarker:(id)dotMarker +{    unichar ch;    NSMutableString *token;    MLKReadtable *readtable; @@ -169,6 +184,16 @@      }    //NSLog (@"--> Interpret token: %@", token); + +  if ([token isEqualToString:@"."]) +    { +      if (dotMarker) +        return dotMarker; +      else +        [NSException raise:@"MLKReaderError" +                     format:@"Unexpectedly read a single dot."]; +    } +    return [self interpretToken:token                 readtable:readtable                 escaped:ever_escaped]; @@ -225,10 +225,12 @@ static id truify (BOOL value)  +(NSArray *) macroexpand_1:(NSArray *)args  {    id form = [args objectAtIndex:0]; -  id env = [args count] > 1 ? [args objectAtIndex:1] : nil; +  id env = [args count] > 1 ? denullify([args objectAtIndex:1]) : nil;    MLKLexicalContext *context = env ? (id)env : (id)[MLKLexicalContext globalContext]; -  if ([context symbolNamesMacro:[form car]]) +  if ([form isKindOfClass:[MLKCons class]] +      && (![form car] || [[form car] isKindOfClass:[MLKSymbol class]]) +      && [context symbolNamesMacro:[form car]])      {        id <MLKFuncallable> macrofun = [context macroForSymbol:[form car]];        form = denullify ([[macrofun applyToArray: @@ -409,4 +411,27 @@ static id truify (BOOL value)    RETURN_VALUE ([cl intern:@"T"]);  } + ++(NSArray *) objc_class_of:(NSArray *)args +{ +  RETURN_VALUE ([[args objectAtIndex:0] class]); +} + ++(NSArray *) objc_subclassp:(NSArray *)args +{ +  RETURN_VALUE (truify ([[args objectAtIndex:0] isSubclassOfClass: +                                                  [args objectAtIndex:1]])); +} + ++(NSArray *) find_objc_class:(NSArray *)args +{ +  RETURN_VALUE (NSClassFromString ([args objectAtIndex:0])); +} + ++(NSArray *) ns_log:(NSArray *)args +{ +  NSString *description = [[args objectAtIndex:0] descriptionForLisp]; +  NSLog (@"%@", description); +  RETURN_VALUE ([args objectAtIndex:0]); +}  @end diff --git a/cond.lisp b/cond.lisp deleted file mode 100644 index 7b3d1cc..0000000 --- a/cond.lisp +++ /dev/null @@ -1,29 +0,0 @@ -(%defun list* args -  (if (null (cdr args)) -      (car args) -      (cons (car args) -            (apply 'list* (cdr args))))) - -(%defmacro let* args -  (let ((form (car args))) -    (let ((bindings (car (cdr form))) -          (body (cdr (cdr form)))) -      (if (null bindings) -          (list* 'let nil body) -          (let ((first-binding (car bindings)) -                (rest (cdr bindings))) -            (list 'let -                  (list first-binding) -                  (list* 'let* rest body))))))) - -(%defmacro cond args -  (let* ((form (car args)) -         (clauses (cdr form)) -         (clause (car clauses)) -         (rest (cdr clauses))) -    (if (null clauses) -        nil -        (list 'if -              (car clause) -              (cons 'progn (cdr clause)) -              (cons 'cond rest))))) diff --git a/destructuring-bind.lisp b/destructuring-bind.lisp new file mode 100644 index 0000000..161390d --- /dev/null +++ b/destructuring-bind.lisp @@ -0,0 +1,114 @@ +(setq lambda-list-keywords +      '(&allow-other-keys &aux &body &environment &key &optional &rest &whole)) + +(%defmacro* d-b (lambda-list environment whole-sym expression . body) +  ;; (ns-log lambda-list) +  `(let* ,(unless whole-sym +            (let ((real-expression expression)) +              (setq whole-sym (gensym "WHOLE") +                    expression (gensym "EXPRESSION")) +              `((,expression ,real-expression) +                (,whole-sym ,expression)))) +     ,(cond ((consp lambda-list) +             (case (car lambda-list) +               (&environment +                `(let ((,(cadr lambda-list) ,environment)) +                   (d-b ,(cddr lambda-list) ,environment ,whole-sym ,expression +                     ,@body))) +               (&aux +                (if (or (endp (cdr lambda-list)) +                        (member (cadr lambda-list) lambda-list-keywords)) +                    `(d-b ,(cdr lambda-list) ,environment ,whole-sym ,expression +                       ,@body) +                    `(let (,(cadr lambda-list)) +                       (d-b (&aux ,@(cddr lambda-list)) ,environment ,whole-sym ,expression +                         ,@body)))) +               (&optional +                (if (or (endp (cdr lambda-list)) +                        (member (cadr lambda-list) lambda-list-keywords)) +                    `(d-b ,(cdr lambda-list) ,environment ,whole-sym ,expression +                       ,@body) +                    (let ((sym (gensym)) +                          (head (car lambda-list))) +                      `(let* ((,sym ,expression) +                              ,@(cond ((atom head) +                                       `((,head (cadr ,sym)))) +                                      ((null (cdr head)) +                                       `((,(car head) (cadr ,sym)))) +                                      ((null (cddr head)) +                                       `((,(car head) (if (null ,sym) +                                                          ,(cadr head) +                                                          (cadr ,sym))))) +                                      (t +                                       `((,(car head) (if (null ,sym) +                                                          ,(cadr head) +                                                          (cadr ,sym))) +                                         (,(caddr head) (not (null ,sym))))))) +                         (d-b (&optional ,@(cddr lambda-list)) ,environment ,whole-sym (cdr ,sym) +                           ,@body))))) +               ((&rest &body) +                (if (member (cadr lambda-list) lambda-list-keywords) +                    `(d-b ,(cdr lambda-list) ,environment ,whole-sym ,expression +                       ,@body) +                    (let ((sym (gensym))) +                      `(let* ((,sym ,expression) +                              (,(cadr lambda-list) ,sym)) +                         (d-b ,(cddr lambda-list) ,environment ,whole-sym ,sym +                           ,@body))))) +               (&whole +                `(let ((,(cadr lambda-list) ,whole-sym)) +                   (d-b ,(cddr lambda-list) ,environment ,whole-sym ,expression +                     ,@body))) +               (&allow-other-keys +                `(d-b ,(cdr lambda-list) ,environment ,whole-sym ,expression +                   ,@body)) +               (&key +                (if (or (endp (cdr lambda-list)) +                        (member (cadr lambda-list) lambda-list-keywords)) +                    `(d-b ,(cdr lambda-list) ,environment ,whole-sym ,expression +                       ,@body) +                    (let* ((sym (gensym)) +                           (value-sym (gensym)) +                           (missing (gensym "MISSING")) +                           (head (cadr lambda-list)) +                           (var (if (consp head) +                                    (if (consp (car head)) +                                        (cadar head) +                                        (car head)) +                                    head)) +                           (keyword-name +                            (if (and (consp head) (consp (car head))) +                                (caar head) +                                (intern (symbol-name var) '#:keyword)))) +                      `(let* ((,sym ,expression) +                              (,value-sym (getf ,sym ,keyword-name ',missing)) +                              ,@(cond ((atom head) +                                       `((,var ,value-sym))) +                                      ((null (cdr head)) +                                       `((,var ,value-sym))) +                                      ((null (cddr head)) +                                       `((,var (if (eq ,value-sym ',missing) +                                                   ,(cadr head) +                                                   ,value-sym)))) +                                      (t +                                       `((,var (if (eq ,value-sym ',missing) +                                                   ,(cadr head) +                                                   ,value-sym)) +                                         (,(caddr head) (not (eq ,value-sym ',missing))))))) +                         (d-b (&key ,@(cddr lambda-list)) ,environment ,whole-sym ,sym +                           ,@body))))) +               (otherwise +                (let ((sym (gensym))) +                  `(let ((,sym ,expression)) +                     (d-b ,(car lambda-list) ,environment ,whole-sym (car ,sym) +                       (d-b ,(cdr lambda-list) ,environment ,whole-sym (cdr ,sym) +                         ,@body))))))) +            ((null lambda-list) +             `(progn ,@body)) +            (t `(let ((,lambda-list ,expression)) +                  ,@body))))) + + +(export '(destructuring-bind lambda-list-keywords +          &allow-other-keys &aux &body &environment &key &optional &rest +          &whole)) @@ -1,5 +1,5 @@  (in-package :common-lisp)  (load "util.lisp") -(load "cond.lisp")  (load "list-functions.lisp") +(load "destructuring-bind.lisp")  (in-package :common-lisp-user) @@ -2,3 +2,198 @@    (list '%fset          (list 'quote (car (cdr (car args))))          (cons '%lambda (cdr (cdr (car args)))))) + +(%defun list* args +  (if (null (cdr args)) +      (car args) +      (cons (car args) +            (apply 'list* (cdr args))))) + +(%defmacro let* args +  (let ((form (car args))) +    (let ((bindings (car (cdr form))) +          (body (cdr (cdr form)))) +      (if (null bindings) +          (list* 'let nil body) +          (let ((first-binding (car bindings)) +                (rest (cdr bindings))) +            (list 'let +                  (list first-binding) +                  (list* 'let* rest body))))))) + +(%defmacro cond args +  (let* ((form (car args)) +         (clauses (cdr form)) +         (clause (car clauses)) +         (rest (cdr clauses))) +    (if (null clauses) +        nil +        (list 'if +              (car clause) +              (cons 'progn (cdr clause)) +              (cons 'cond rest))))) + +(%defun not args +  (if (null (car args)) t nil)) + +(%defun make-%defmacro*-body args +  (let ((lambda-list (car args)) +        (lambda-list-name (car (cdr args))) +        (body (car (cdr (cdr args))))) +    (cond ((null lambda-list) body) +          ((not (listp lambda-list)) +           (list +            (list* 'let +                   (list (list lambda-list lambda-list-name)) +                   body))) +          (t (let ((lambda-symbol (car lambda-list)) +                   (rest-lambda-list (cdr lambda-list)) +                   (rest-name (gensym))) +               (list +                (list* 'let +                       (list (list lambda-symbol +                                   (list 'car lambda-list-name)) +                             (list rest-name +                                   (list 'cdr lambda-list-name))) +                       (make-%defmacro*-body (cdr lambda-list) +                                             rest-name +                                             body)))))))) + +(%defmacro %defmacro* args +  (let* ((form (car args)) +         (real-args (cdr form))) +    (let ((name (car real-args)) +          (lambda-list (car (cdr real-args))) +          (body (cdr (cdr real-args))) +          (macro-lambda-list-name (gensym)) +          (lambda-list-name (gensym))) +      (list '%defmacro +            name +            macro-lambda-list-name +            (list* 'let +                   (list (list lambda-list-name +                               (list 'cdr +                                     (list 'car macro-lambda-list-name)))) +                   (make-%defmacro*-body lambda-list lambda-list-name body)))))) + +(%defmacro %defun* args +  (let* ((form (car args)) +         (real-args (cdr form))) +    (let ((name (car real-args)) +          (lambda-list (car (cdr real-args))) +          (body (cdr (cdr real-args))) +          (lambda-list-name (gensym))) +      (list* '%defun +             name +             lambda-list-name +             (make-%defmacro*-body lambda-list lambda-list-name body))))) + +(%defmacro* and expressions +  (cond ((null expressions) t) +        ((null (cdr expressions)) (car expressions)) +        (t (list 'if +                 (car expressions) +                 (cons 'and (cdr expressions)) +                 nil)))) + +(%defmacro* or expressions +  (cond ((null expressions) nil) +        ((null (cdr expressions)) (car expressions)) +        (t (let ((expr-sym (gensym))) +             (list 'let +                   (list (list expr-sym (car expressions))) +                   (list 'if +                         expr-sym +                         expr-sym +                         (cons 'or (cdr expressions)))))))) + +(%defun* %reverse-helper (list stack) +  (if (null list) +      stack +      (%reverse-helper (cdr list) (cons (car list) stack)))) + +(%defun* reverse (list) +  (%reverse-helper list nil)) + +(%defun* %append-helper (reversed-list1 list2) +  (if (null reversed-list1) +      list2 +      (%append-helper (cdr reversed-list1) (cons (car reversed-list1) list2)))) + +(%defun* %append-two-lists (list1 list2) +  (%append-helper (reverse list1) list2)) + +(%defun* %append (lists) +  (if (null (cdr lists)) +      (car lists) +      (let ((first-list (car lists)) +            (second-list (car (cdr lists))) +            (rest (cdr (cdr lists)))) +        (%append (list* (%append-two-lists first-list second-list) rest))))) + +(%defun append lists +  (%append lists)) + +(%defmacro* sys::quasiquote (object) +  (if (not (consp object)) +      (list 'quote object) +      (cond ((eq 'sys::unquote (car object)) (car (cdr object))) +            ((eq 'sys::quasiquote (car object)) (list 'quote object)) +            ((and (consp (car object)) +                  (eq 'sys::unquote-splicing (car (car object)))) +             (list 'append +                   (car (cdr (car object))) +                   (list 'sys::quasiquote (cdr object)))) +            (t (list 'cons +                     (list 'sys::quasiquote (car object)) +                     (list 'sys::quasiquote (cdr object))))))) + +(%defun* %member (item list) +  (and list +       (or (and (eq item (car list)) list) +           (%member item (cdr list))))) + +(%defmacro* case (object . clauses) +  (let ((this-clause (car clauses)) +        (rest (cdr clauses)) +        (object-sym (gensym))) +    (if (null clauses) +        nil +        (if (and (null rest) +                 (or (eq (car this-clause) t) +                     (eq (car this-clause) 'otherwise))) +            `(progn ,@(cdr this-clause)) +            `(let ((,object-sym ,object)) +               (if ,(if (listp (car this-clause)) +                        `(%member ,object-sym +                                  (quote ,(car this-clause))) +                        `(eq ,object-sym +                             (quote ,(car this-clause)))) +                   (progn ,@(cdr this-clause)) +                   (case ,object-sym ,@rest))))))) + +(%defun* list-eqp (list1 list2) +  "Not really EQUALP (only works on trees of symbols)." +  (if (and (consp list1) (consp list2)) +      (and (list-eqp (car list1) (car list2)) +           (list-eqp (cdr list1) (cdr list2))) +      (eq list1 list2))) + +(%defun* macroexpand (object . rest) +  (let* ((env (if rest (car rest) nil)) +         (expansion-1 (macroexpand-1 object env)) +         (expansion-2 (macroexpand-1 expansion-1 env))) +    (if (list-eqp expansion-1 expansion-2) +        expansion-1 +        (macroexpand expansion-2)))) + +(%defun* macroexpand-all (object . rest) +  (let* ((env (if rest (car rest) nil))) +    (if (consp object) +        (let ((expansion (macroexpand object env))) +          (cons (macroexpand-all (car expansion)) +                (macroexpand-all (cdr expansion)))) +        object))) + +(%defmacro* unless (test . body) +  `(if (not ,test) (progn ,@body) nil))  | 
