home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Source / MiscMergeKit / MiscIfStack.m < prev    next >
Encoding:
Text File  |  1995-07-08  |  5.0 KB  |  154 lines

  1. //
  2. //    MiscIfStack.m -- a simple data container for tracking if/then constructs
  3. //        Written by Don Yacktman Copyright (c) 1995 by Don Yacktman.
  4. //                Version 1.0.  All rights reserved.
  5. //        This notice may not be removed from this source code.
  6. //
  7. //    This object is included in the MiscKit by permission from the author
  8. //    and its use is governed by the MiscKit license, found in the file
  9. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  10. //    for a list of all applicable permissions and restrictions.
  11. //    
  12.  
  13.  
  14. #import "_MiscIfPlaceHolder.h"
  15. #import <misckit/MiscIfStack.h>
  16.  
  17. @implementation MiscIfStack
  18. /*" A MiscIfStack is a specialized stack which may be used in a
  19. dynamic interpreter to implement if/then/else/endif constructs.
  20. The syntax is expected to have an "endif" match every single
  21. "if" token.  The "else" token is optional.  The "if", "else", and
  22. "endif" divide the interpreted code into blocks.  The MiscIfStack
  23. can tell you whether or not the current block should be executed.
  24. This includes correct handling of nested ifs.
  25.  
  26. To use a MiscIfStack, first send a -#{reset} message.  Then, whenever
  27. an "if" token is encountered in the parsing, send a -#{startIf:}
  28. message to the MiscIfStack.  If the conditional evaluated true, use YES
  29. as the argument.  Us NO if it evaluated falsely.  When (and if) an
  30. "else" token is encountered, send a -#{startElse} message.  Finally,
  31. when the "endif" toekn is encountered, send a -#{endIf} message.
  32.  
  33. To determine if the current block should be executed, simply query
  34. the MiscIfStack with a -#{currentConditionalIsActive} message.  If
  35. YES is returned, then the code should be executed.
  36. "*/
  37.  
  38. - reset
  39. /*" Clears the MiscIfStack.  This should be called whenever a new
  40. program is started.  Returns self.
  41. "*/
  42. {
  43.     [self freeObjects];
  44.     [self empty];
  45.     return self;
  46. }
  47.  
  48. - startIf:(BOOL)isActive
  49. /*" Starts an "if" block.  If the "if" evaluates to true, then %{isActive}
  50. should be YES, NO otherwise.  This will be used to determine the current
  51. status of the MiscIfStack.  Returns self, or nil if an error occurs.
  52. "*/
  53. {
  54.     id tempIf = [[_MiscIfPlaceHolder alloc] init];
  55.  
  56.     [tempIf setIfType:MISC_IF_START];
  57.     if ([self currentConditionalIsActive]) {
  58.         [tempIf setActivity:(isActive ? MISC_IF_ACTIVE : MISC_IF_INACTIVE)];
  59.     } else {
  60.         [tempIf setActivity:MISC_IF_DEAD];
  61.     }
  62.     if ([self pushObject:tempIf]) return self;
  63.     return nil;
  64. }
  65.  
  66. - startElse
  67. /*" Begins an "else" block, changing the status to be the opposite
  68. of the "if" block this is paired to.  Returns "self" if successful
  69. and "nil" if an error occurs.  Errors include two "else" tokens in
  70. a row or an "else" without an accompanying "if" token.
  71. "*/
  72. {
  73.     id tempIf, lastIf = [self lastObject];
  74.     Misc_IF_Activity newActivity;
  75.  
  76.     if ([lastIf ifType] == MISC_IF_ELSE) {
  77.         [self doubleElseIfError];
  78.         return nil;
  79.     }
  80.     if (!lastIf) {
  81.         [self elseWithoutIfError];
  82.         return nil;
  83.     }
  84.     tempIf = [[_MiscIfPlaceHolder alloc] init];
  85.     [tempIf setIfType:MISC_IF_ELSE];
  86.     switch ([lastIf activity]) {
  87.         case MISC_IF_ACTIVE : newActivity = MISC_IF_INACTIVE; break;
  88.         case MISC_IF_INACTIVE : newActivity = MISC_IF_ACTIVE; break;
  89.         // dead ifs stay dead in the else.  It means that this if/else
  90.         // pair is inside of a conditional that evaluated false.
  91.         default : newActivity = MISC_IF_DEAD; break;
  92.     }
  93.     [tempIf setActivity:newActivity];
  94.     if ([self pushObject:tempIf]) return self;
  95.     return nil;
  96. }
  97.  
  98. - endIf
  99. /*" Ends an "if-else" block, returning the MiscIfStack to the status
  100. of the block before the "if" to be cleared was encountered.  Returns
  101. self if successful and nil if there was an error, such as an "endif"
  102. without a matching "if".
  103. "*/
  104. {
  105.     if (![self lastObject]) { // error-- "endif" without "if"
  106.         [self endWithoutIfError];
  107.         return nil;
  108.     }
  109.     // Discard an "else" if there is one.
  110.     if ([[self lastObject] ifType] == MISC_IF_ELSE) {
  111.         [[self popObject] free];
  112.     }
  113.     // And discard the "if" that matches us.  We don't need to check
  114.     // since the checking when we push guarantees things for us.
  115.     [[self popObject] free];
  116.     return self;
  117. }
  118.  
  119. - (BOOL)currentConditionalIsActive
  120. /*" Returns YES if the "if" or "else" block on top of the stack
  121. evaluates true and should be executed.  Returns NO otherwise.
  122. "*/
  123. { // Inactive if "dead" or explicitly inactive.  Active if explicitly active.
  124.     if ([self count] < 1) return YES;
  125.     return ([[self lastObject] activity] == MISC_IF_ACTIVE);
  126. }
  127.  
  128. - (void)endWithoutIfError
  129. /*" Prints a diagnostic error message to the console if
  130. an "endif" token is found that does not have a matching "if" token.
  131. "*/
  132. {
  133.     fprintf(stderr, "MiscIfStack:  \"endif\" without \"if\" encountered.\n");
  134. }
  135.  
  136. - (void)elseWithoutIfError
  137. /*" Prints a diagnostic error message to the console if
  138. an "else" token is found that does not have a matching "if" token.
  139. "*/
  140. {
  141.     fprintf(stderr, "MiscIfStack:  \"else\" without \"if\" encountered.\n");
  142. }
  143.  
  144. - (void)doubleElseIfError
  145. /*" Prints a diagnostic error message to the console if
  146. two "else" tokens are found in a row.  This is syntactically the
  147. same as having an "else" without a matching "if".
  148. "*/
  149. {
  150.     fprintf(stderr, "MiscIfStack:  \"else\" use twice in a row.\n");
  151. }
  152.  
  153. @end
  154.