/* gib_semantics.c GIB tree handling functions Copyright (C) 2003 Brian Koropoff Author: Brian Koropoff Date: #DATE# 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 2 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, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #include #include "QF/qtypes.h" #include "gib_tree.h" #include "gib_parse.h" #include "gib_semantics.h" static gib_tree_t * GIB_Semantic_Normal_To_Lines (gib_tree_t * tokens, const char *program, gib_tree_flags_t flags, unsigned int start, unsigned int end) { gib_tree_t *mainline, *lines = 0, *embedded, *token, *temp; /* Normal tokens */ // Create main line struct now as an anchor lines = mainline = GIB_Tree_New (TREE_T_CMD); for (token = tokens; token; token = token->next) { // Normal token? if (token->delim == ' ' || token->delim == '(') { // Check for embedded commands/variables embedded = GIB_Parse_Embedded (token); if (gib_parse_error) { GIB_Tree_Unref (&mainline); return 0; } if (embedded) { // Find end of embedded commands for (temp = embedded; temp->next; temp = temp->next); // Link it in temp->next = lines; lines = embedded; // Mark token for processing at runtime token->flags |= TREE_A_EMBED; } else if (token->children) // Mark token for processing at runtime token->flags |= TREE_A_EMBED; } } // Fill in info for primary line mainline->str = program; mainline->flags = flags; mainline->start = start; mainline->end = end; mainline->children = tokens; // Check for assignment if (tokens->next && tokens->next->delim == ' ' && !strcmp (tokens->next->str, "=")) mainline->type = TREE_T_ASSIGN; return lines; } static gib_tree_t * GIB_Semantic_Process_Conditional (gib_tree_t *tokens) { gib_tree_t *lines = 0, *temp, *cur, *embedded; // Check for embeddeds in first token lines = GIB_Parse_Embedded (tokens); if (gib_parse_error) return 0; if (lines || tokens->children) tokens->flags |= TREE_A_EMBED; // Iterate through all tokens concatenated to the first for (cur = tokens->next; cur && cur->flags & TREE_A_CONCAT; cur = cur->next) { embedded = GIB_Parse_Embedded (cur); if (gib_parse_error) return 0; if (embedded) { for (temp = embedded; temp->next; temp = temp->next); temp->next = lines; lines = embedded; cur->flags |= TREE_A_EMBED; } else if (cur->children) cur->flags |= TREE_A_EMBED; } return lines; } static gib_tree_t * GIB_Semantic_If_To_Lines (gib_tree_t **tokens, const char *program, gib_tree_flags_t flags, gib_tree_t *end) { gib_tree_t *token = *tokens, *lines, *temp, **next = &lines, *conditional; gib_tree_t *a_block; // Sanity checking if (flags & TREE_L_EMBED) { GIB_Parse_Error ("if statements may not be used in embedded commands.", token->start); return 0; } if (!token->next || !token->next->next) { GIB_Parse_Error ("malformed if statement; not enough arguments", token->start); return 0; } for (a_block = token->next->next; a_block->flags & TREE_A_CONCAT; a_block = a_block->next) if (!a_block->next) { GIB_Parse_Error ("malformed if statement; not enough arguments", token->start); return 0; } if (a_block->delim != '{') { GIB_Parse_Error ("malformed if statement; second argument is not a program block", token->next->next->start); return 0; } if (a_block->next) { // Else statement? if (a_block->next->delim != ' ' || strcmp (a_block->next->str, "else") || (a_block->next->next && a_block->next->next->flags & TREE_A_CONCAT)) { GIB_Parse_Error ("malformed if statement; expected \"else\" for third argument", token->next->next->next->start); return 0; } if (!a_block->next->next) { GIB_Parse_Error ("malformed if statement; expected arguments after \"else\"", token->next->next->next->start); return 0; } } // Process embedded stuff on conditional argument lines = GIB_Semantic_Process_Conditional (token->next); if (gib_parse_error) return 0; if (lines) { for (temp = lines; temp->next; temp = temp->next); next = &temp->next; } // Create conditional instruction conditional = GIB_Tree_New (TREE_T_COND); conditional->children = token; conditional->start = token->start; conditional->end = token->next->end; conditional->str = program; conditional->flags = token->str[2] ? flags | TREE_L_NOT : flags; // Link it in *next = conditional; next = &conditional->next; // Move program block inline *next = a_block->children; a_block->children = 0; // Find end of program block for (temp = *next; temp->next; temp = temp->next); next = &temp->next; // Add jump to end point *next = GIB_Tree_New (TREE_T_JUMP); (*next)->jump = end; conditional->jump = *next; next = &(*next)->next; // Is there an else statement? if (a_block->next) { temp = a_block->next; token = a_block->next->next; // Is this a program block? if (token->delim == '{') { // Move program block inline *next = token->children; token->children = 0; *tokens = 0; } else { // Cut off part we don't use temp->next = 0; // Indicate to caller that more tokens remain *tokens = token; // String will get used again, make a personal copy conditional->str = strdup (program); } } else // No tokens left *tokens = 0; return lines; } static gib_tree_t * GIB_Semantic_While_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_flags_t flags) { gib_tree_t *lines, **next = &lines, *temp, *conditional, *endp; gib_tree_t *a_block; // Sanity checking if (flags & TREE_L_EMBED) { GIB_Parse_Error ("while statements may not be used in embedded commands", tokens->start); return 0; } if (!tokens->next || !tokens->next->next) { GIB_Parse_Error ("malformed while statement; incorrect number of arguments", tokens->start); return 0; } for (a_block = tokens->next->next; a_block->flags & TREE_A_CONCAT; a_block = a_block->next) if (!a_block->next) { GIB_Parse_Error ("malformed while statement; incorrect number of arguments", tokens->start); return 0; } if (a_block->delim != '{') { GIB_Parse_Error ("malformed while statement; expected program block for second argument", a_block->start); return 0; } // Process embedded stuff on conditional argument lines = GIB_Semantic_Process_Conditional (tokens->next); if (gib_parse_error) return 0; if (lines) { for (temp = lines; temp->next; temp = temp->next); next = &temp->next; } // Create conditional instruction conditional = GIB_Tree_New (TREE_T_COND); conditional->children = tokens; conditional->start = tokens->start; conditional->end = tokens->next->end; conditional->str = program; conditional->flags = flags; // Link it in *next = conditional; next = &conditional->next; // Create end point (for 'break' commands) endp = GIB_Tree_New (TREE_T_NOP); // Move program block inline *next = a_block->children; a_block->children = 0; // Find end of program block for (temp = *next; temp->next; temp = temp->next) { // Check for break or continue if (!temp->jump && temp->children) { if (!strcmp (temp->children->str, "break")) { temp->type = TREE_T_JUMP; temp->jump = endp; } else if (!strcmp (temp->children->str, "continue")) { temp->type = TREE_T_JUMP; temp->jump = lines; } } } next = &temp->next; // Add jump back to top of loop temp = GIB_Tree_New (TREE_T_JUMP); temp->jump = lines; *next = temp; next = &temp->next; // Attach landing point for 'break' instructions *next = endp; // This is our last instruction, set it as escape for loop conditional->jump = endp; return lines; } static gib_tree_t * GIB_Semantic_For_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_flags_t flags) { gib_tree_t *lines = 0, **next = &lines, *temp, *endp, *forinst, *embedded; gib_tree_t *a_in, *a_block; // Do sanity checks if (flags & TREE_L_EMBED) { GIB_Parse_Error ("for statements may not be used in embedded commands.", tokens->start); return 0; } if (!tokens->next) { GIB_Parse_Error ("malformed for statement; not enough arguments.", tokens->start); return 0; } for (a_in = tokens->next; a_in->delim != ' ' || strcmp (a_in->str, "in"); a_in = a_in->next) { if (!a_in->next) { GIB_Parse_Error ("malformed for statement; expected \"in\".", tokens->start); return 0; } if (a_in->flags & TREE_A_EMBED || a_in->flags & TREE_A_EXPAND) a_in->flags &= ~(TREE_A_EMBED | TREE_A_EXPAND); } if (!a_in->next) { GIB_Parse_Error ("malformed for statement; expected arguments after \"in\".", a_in->start); return 0; } for (a_block = a_in->next; a_block->next; a_block = a_block->next); if (a_block->delim != '{') { GIB_Parse_Error ("malformed for statement; expected program block as last argument.", a_block->start); return 0; } for (a_in = a_in->next; a_in != a_block; a_in = a_in->next) { embedded = GIB_Parse_Embedded (a_in); if (gib_parse_error) return 0; if (embedded) { for (temp = embedded; temp->next; temp = temp->next); temp->next = lines; lines = embedded; a_in->flags |= TREE_A_EMBED; } else if (a_in->children) a_in->flags |= TREE_A_EMBED; } if (lines) { for (temp = lines; temp->next; temp = temp->next); next = &temp->next; } forinst = GIB_Tree_New (TREE_T_CMD); forinst->children = tokens; forinst->start = tokens->start; forinst->end = a_block->start; forinst->str = program; forinst->flags = flags; *next = forinst; forinst->next = GIB_Tree_New (TREE_T_FORNEXT); forinst = forinst->next; next = &forinst->next; *next = a_block->children; a_block->children = 0; endp = GIB_Tree_New (TREE_T_NOP); // Find end of program block for (temp = *next; temp->next; temp = temp->next) { // Check for break or continue if (!temp->jump && temp->children) { if (!strcmp (temp->children->str, "break")) { temp->type = TREE_T_JUMP; temp->jump = endp; } else if (!strcmp (temp->children->str, "continue")) { temp->type = TREE_T_JUMP; temp->jump = forinst; } } } next = &temp->next; *next = GIB_Tree_New (TREE_T_JUMP); (*next)->jump = forinst; (*next)->next = endp; forinst->jump = endp; return lines; } gib_tree_t * GIB_Semantic_Tokens_To_Lines (gib_tree_t *tokens, const char *program, gib_tree_flags_t flags, unsigned int start, unsigned int end) { gib_tree_t *lines, **next = &lines, *temp, *endp = 0; // If we are being weird, don't even bother trying to match special statements if (tokens->next && tokens->next->flags & TREE_A_CONCAT) return GIB_Semantic_Normal_To_Lines (tokens, program, flags, start, end); // Handle if statements if (!strcmp (tokens->str, "if") || !strcmp (tokens->str, "ifnot")) { // Create landing pad where all program blocks in // a chained if/else structure will jump to after // completing so that only one block is executed. endp = GIB_Tree_New (TREE_T_NOP); do { // Link in output from if statement handler *next = GIB_Semantic_If_To_Lines (&tokens, program, flags, endp); if (gib_parse_error) goto ERROR; // Find end of output for (temp = *next; temp->next; temp = temp->next); next = &temp->next; } while (tokens && (!strcmp (tokens->str, "if") || !strcmp (tokens->str, "ifnot"))); } if (!tokens) // Yes, goto is evil. See if I care. goto DONE; if (!strcmp (tokens->str, "while")) *next = GIB_Semantic_While_To_Lines (tokens, program, flags); else if (!strcmp (tokens->str, "for")) *next = GIB_Semantic_For_To_Lines (tokens, program, flags); else *next = GIB_Semantic_Normal_To_Lines (tokens, program, flags, start, end); next = &(*next)->next; if (gib_parse_error) goto ERROR; DONE: // Connect landing pad, if one exists if (endp) *next = endp; return lines; ERROR: if (endp) GIB_Tree_Unref (&endp); return 0; }