home *** CD-ROM | disk | FTP | other *** search
- //
- // MiscIfStack.m -- a simple data container for tracking if/then constructs
- // Written by Don Yacktman Copyright (c) 1995 by Don Yacktman.
- // Version 1.0. All rights reserved.
- // This notice may not be removed from this source code.
- //
- // This object is included in the MiscKit by permission from the author
- // and its use is governed by the MiscKit license, found in the file
- // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
- // for a list of all applicable permissions and restrictions.
- //
-
-
- #import "_MiscIfPlaceHolder.h"
- #import <misckit/MiscIfStack.h>
-
- @implementation MiscIfStack
- /*" A MiscIfStack is a specialized stack which may be used in a
- dynamic interpreter to implement if/then/else/endif constructs.
- The syntax is expected to have an "endif" match every single
- "if" token. The "else" token is optional. The "if", "else", and
- "endif" divide the interpreted code into blocks. The MiscIfStack
- can tell you whether or not the current block should be executed.
- This includes correct handling of nested ifs.
-
- To use a MiscIfStack, first send a -#{reset} message. Then, whenever
- an "if" token is encountered in the parsing, send a -#{startIf:}
- message to the MiscIfStack. If the conditional evaluated true, use YES
- as the argument. Us NO if it evaluated falsely. When (and if) an
- "else" token is encountered, send a -#{startElse} message. Finally,
- when the "endif" toekn is encountered, send a -#{endIf} message.
-
- To determine if the current block should be executed, simply query
- the MiscIfStack with a -#{currentConditionalIsActive} message. If
- YES is returned, then the code should be executed.
- "*/
-
- - reset
- /*" Clears the MiscIfStack. This should be called whenever a new
- program is started. Returns self.
- "*/
- {
- [self freeObjects];
- [self empty];
- return self;
- }
-
- - startIf:(BOOL)isActive
- /*" Starts an "if" block. If the "if" evaluates to true, then %{isActive}
- should be YES, NO otherwise. This will be used to determine the current
- status of the MiscIfStack. Returns self, or nil if an error occurs.
- "*/
- {
- id tempIf = [[_MiscIfPlaceHolder alloc] init];
-
- [tempIf setIfType:MISC_IF_START];
- if ([self currentConditionalIsActive]) {
- [tempIf setActivity:(isActive ? MISC_IF_ACTIVE : MISC_IF_INACTIVE)];
- } else {
- [tempIf setActivity:MISC_IF_DEAD];
- }
- if ([self pushObject:tempIf]) return self;
- return nil;
- }
-
- - startElse
- /*" Begins an "else" block, changing the status to be the opposite
- of the "if" block this is paired to. Returns "self" if successful
- and "nil" if an error occurs. Errors include two "else" tokens in
- a row or an "else" without an accompanying "if" token.
- "*/
- {
- id tempIf, lastIf = [self lastObject];
- Misc_IF_Activity newActivity;
-
- if ([lastIf ifType] == MISC_IF_ELSE) {
- [self doubleElseIfError];
- return nil;
- }
- if (!lastIf) {
- [self elseWithoutIfError];
- return nil;
- }
- tempIf = [[_MiscIfPlaceHolder alloc] init];
- [tempIf setIfType:MISC_IF_ELSE];
- switch ([lastIf activity]) {
- case MISC_IF_ACTIVE : newActivity = MISC_IF_INACTIVE; break;
- case MISC_IF_INACTIVE : newActivity = MISC_IF_ACTIVE; break;
- // dead ifs stay dead in the else. It means that this if/else
- // pair is inside of a conditional that evaluated false.
- default : newActivity = MISC_IF_DEAD; break;
- }
- [tempIf setActivity:newActivity];
- if ([self pushObject:tempIf]) return self;
- return nil;
- }
-
- - endIf
- /*" Ends an "if-else" block, returning the MiscIfStack to the status
- of the block before the "if" to be cleared was encountered. Returns
- self if successful and nil if there was an error, such as an "endif"
- without a matching "if".
- "*/
- {
- if (![self lastObject]) { // error-- "endif" without "if"
- [self endWithoutIfError];
- return nil;
- }
- // Discard an "else" if there is one.
- if ([[self lastObject] ifType] == MISC_IF_ELSE) {
- [[self popObject] free];
- }
- // And discard the "if" that matches us. We don't need to check
- // since the checking when we push guarantees things for us.
- [[self popObject] free];
- return self;
- }
-
- - (BOOL)currentConditionalIsActive
- /*" Returns YES if the "if" or "else" block on top of the stack
- evaluates true and should be executed. Returns NO otherwise.
- "*/
- { // Inactive if "dead" or explicitly inactive. Active if explicitly active.
- if ([self count] < 1) return YES;
- return ([[self lastObject] activity] == MISC_IF_ACTIVE);
- }
-
- - (void)endWithoutIfError
- /*" Prints a diagnostic error message to the console if
- an "endif" token is found that does not have a matching "if" token.
- "*/
- {
- fprintf(stderr, "MiscIfStack: \"endif\" without \"if\" encountered.\n");
- }
-
- - (void)elseWithoutIfError
- /*" Prints a diagnostic error message to the console if
- an "else" token is found that does not have a matching "if" token.
- "*/
- {
- fprintf(stderr, "MiscIfStack: \"else\" without \"if\" encountered.\n");
- }
-
- - (void)doubleElseIfError
- /*" Prints a diagnostic error message to the console if
- two "else" tokens are found in a row. This is syntactically the
- same as having an "else" without a matching "if".
- "*/
- {
- fprintf(stderr, "MiscIfStack: \"else\" use twice in a row.\n");
- }
-
- @end
-