void doOp( node_t* node, node_t* parent) { // DEBUG //fprintf(stdout, "doOp(%s)", node->data); uint32_t* result = malloc(sizeof(uint32_t)); uint32_t* tall1 = (uint32_t*)node->children[0]->data; uint32_t* tall2 = (uint32_t*)node->children[1]->data; char* c = (char*)node->data; if(*c == '+') { *result = *tall1 + *tall2; } if(*c == '-') { *result = *tall1 - *tall2; } if(*c == '*') { *result = *tall1 * *tall2; } if(*c == '/') { *result = *tall1 / *tall2; } if(*c == '^') { *result = (int)pow((double)*tall1, *tall2); } // DEBUG //fprintf(stdout, "%d %c %d = %d\n", *tall1, *c, *tall2, *result); free(node->data); node_finalize(node->children[0]); node_finalize(node->children[1]); //free(node->children); node->n_children = 0; node->data = result; node->type = integer_n; }
void prune_node(node_t* node, node_t** simpler) { if(node == NULL) { // update simpler so that if we are pruning, // the pointer pointing to this node, will point to this node instead *simpler = node; return; } if(is_prunable(node)) { // update the pointer pointing to the node we want to point to instead //prune_node(node->children[0], simpler); for(int i = 0; i < node->n_children; i++) { prune_node(node->children[i], simpler); } // clean up node we are removing node_finalize(node); } else { // recurse down the children and update pointers for (int i = 0; i < node->n_children; ++i) { node_t* child = node->children[i]; prune_node(child, simpler); // update the child pointer we are exploring, in case it was prunable node->children[i] = *simpler; } *simpler = node; } }
void destroy_subtree ( node_t *discard ) { if ( discard != NULL ) { for ( uint32_t i = 0; i < discard->n_children; i++ ) destroy_subtree ( discard->children[i] ); node_finalize ( discard ); } }
/* Recursively remove the entire tree rooted at a node */ void destroy_subtree ( node_t *discard ) { if( discard == NULL ) return; node_finalize( discard ); free ( discard ); }
void destroy_subtree(node_t *discard) { if (!discard) return; for (int i=0; i < discard->n_children; i++) { destroy_subtree((node_t*)discard->children + i); } node_finalize(discard); }
void destroy_subtree ( node_t *discard ){ if(discard == NULL) return; for(int i =0; i < discard->n_children; i++){ destroy_subtree(discard->children[i]); } node_finalize(discard); }
void destroy_subtree ( FILE *output, node_t *discard ) { if ( discard != NULL ) { for ( int i=0; i<discard->n_children; i++ ) destroy_subtree ( output, discard->children[i] ); if( output != NULL) fprintf ( output, "Freeing %s\n", discard->nodetype.text ); node_finalize ( discard ); } }
void simplify_tree ( node_t **simplified, node_t *root ) { node_t *result = root; /* * First, we ensure that we have a node to look at. This has the * convenient side-effect that optional elements in the syntax * remain marked by a NULL placeholder in the tree, keeping it simple * to recognize the structures imposed by the grammar. */ if ( root != NULL ) { /* Recur before treating any single node: depth-first traversal */ for ( uint32_t i=0; i<root->n_children; i++ ) simplify_tree ( &root->children[i], root->children[i] ); /* Here is where we do something to the lowest nodes in the tree */ switch ( root->type.index ) { /* * These types have only syntactic value, so we can throw * them out now: * STATEMENT always has one child, which can identify itself * PARAMETER_LIST only serves to make variable lists optional * in function declarations * ARGUMENT_LIST does the same thing for function calls */ case STATEMENT: case PRINT_ITEM: case PARAMETER_LIST: case ARGUMENT_LIST: result = root->children[0]; node_finalize ( root ); break; /* * Print statements always have exactly one PRINT_LIST child. * Since we are done with the recursive list definition, * its descendants may instead be children of the print statement * itself now. (Quick hack - it's easier to rename the list node * and eliminate the old statement than to copy/move all children.) */ case PRINT_STATEMENT: result = root->children[0]; result->type = root->type; node_finalize ( root ); break; /* * DECLARATION_LIST can be NULL altogether, but we preserved * that at the beginning of the function. This has a somewhat * nasty side-effect in the grammar, however, as it gets a * different structure from the other lists when it DOES exist * (such as here). Other lists have a single-element list with * the last list item at the bottom of the tree, whereas * declaration lists have a 2-element list with NULL and the * last item. The code that follows molds the bottom of a * declaration list into the same form as the other lists, so * the rest can be handled by the otherwise standard * list-flattening code. */ case DECLARATION_LIST: if ( root->children[0] == NULL ) { root->children[0] = root->children[1]; root->n_children--; root->children = realloc ( root->children, root->n_children * sizeof(node_t *) ); } /* NB! There is no 'break' here on purpose - since the * declaration list is now in the standard form, we WANT * control to fall through to the standard list-handling * code below. */ /* * All these lists have the same structure, so general flattening * is quite simple: extend the left child's list with the right * child, and substitute the parent. */ case FUNCTION_LIST: case STATEMENT_LIST: case PRINT_LIST: case EXPRESSION_LIST: case VARIABLE_LIST: if ( root->n_children >= 2 ) { result = root->children[0]; uint32_t n = (result->n_children += 1); result->children = realloc ( result->children, n * sizeof(node_t *) ); result->children[n-1] = root->children[1]; node_finalize ( root ); } break; case EXPRESSION: switch ( root->n_children ) { case 1: if ( root->children[0]->type.index == INTEGER ) { /* Single integers */ result = root->children[0]; if ( root->data != NULL ) /* Negative constants */ *((int32_t *)result->data) *= -1; node_finalize ( root ); } else if ( root->data == NULL ) { /* Single variables, parentheses, etc. */ result = root->children[0]; node_finalize ( root ); } break; case 2: /* Constant binary expressions */ if ( root->children[0]->type.index == INTEGER && root->children[1]->type.index == INTEGER && root->data != NULL ) { result = root->children[0]; int32_t *a = result->data, *b = root->children[1]->data; switch ( *((char *)root->data) ) { case '+': *a += *b; break; case '-': *a -= *b; break; case '*': *a *= *b; break; case '/': *a /= *b; break; case '^': if (*b < 0 && *a != 1) *a = 0; else { *a = 1; for (int32_t x=*a, c=*b; c > 0; c--) *a *= x; } break; } node_finalize ( root->children[1] ); node_finalize ( root ); } break; } break; } } *simplified = result; }
node_t* simplify_tree ( node_t* node ) { if ( node != NULL ){ // Recursively simplify the children of the current node for ( uint32_t i=0; i<node->n_children; i++ ){ node->children[i] = simplify_tree ( node->children[i] ); } // After the children have been simplified, we look at the current node // What we do depend upon the type of node switch ( node->type.index ) { // These are lists which needs to be flattened. Their structure // is the same, so they can be treated the same way. case FUNCTION_LIST: case STATEMENT_LIST: case PRINT_LIST: case EXPRESSION_LIST: case VARIABLE_LIST: if(node->n_children > 1){ node_t *temp, *simplified; temp = node->children[node->n_children - 1]; node->n_children += simplified->n_children; node->children = realloc(node->children, (1 + simplified->n_children)*sizeof(node_t)); for(int i = 0; i < simplified->n_children; i++){ node->children[i] = simplified->children[i]; } node->children[simplified->n_children] = temp; } else{ node->type = node->children[0]->type; node->data = node->children[0]->data; node->entry = node->children[0]->entry; node->n_children = 0; node_finalize(node->children[0]); } break; // Declaration lists should also be flattened, but their stucture is sligthly // different, so they need their own case case DECLARATION_LIST: if(node->n_children > 1){ node_t *temp, *simplified; temp = node->children[node->n_children -1]; node->n_children += simplified->n_children; node->children = realloc(node->children, (1+simplified->n_children)*sizeof(node_t)); for(int i = 0; i < simplified->n_children; i++){ node->children[i] = simplified->children[i]; } node->children[simplified->n_children] = temp; } else if (node->n_children == 1){ node->type = node->children[0]->type; node->data = node->children[0]->data; node->entry = node->children[0]->entry; node->n_children = 0; node_finalize(node->children[0]); } break; // These have only one child, so they are not needed case STATEMENT: case PARAMETER_LIST: case ARGUMENT_LIST: node->type = node->children[0]->type; node->data = node->children[0]->data; node->entry = node->children[0]->entry; node->n_children = 0; node_finalize(node->children[0]); break; // Expressions where both children are integers can be evaluated (and replaced with // integer nodes). Expressions whith just one child can be removed (like statements etc above) case EXPRESSION: if(node->n_children = 1){ node->type = node->children[0]->type; node->data = node->children[0]->data; node->entry = node->children[0]->entry; node->n_children = 0; node_finalize(node->children[0]); } else if( node->n_children = 2){ if(node->children[0]->type.index == INTEGER && node->children[0]->type.index == INTEGER){ node->type = node->children[0]->type; int *a = (node->children[0]->data); int *b = (node->children[0]->data); int *result = malloc(sizeof(int)); { switch(((char *)node->data)[0]){ case '+': *result = *a + *b; break; case '-': *result = *a - *b; break; case '*': *result = (*a) * (*b); break; case '/': *result = (*a) / (*b); break; } } node->data = &result; node->entry = node->children[0]->entry; node->n_children = 0; node_finalize(node->children[0]); node_finalize(node->children[1]); } } break; } } return node; }