/* * Copyright 2007-2018 Adrian Thurston * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include "internal.h" void colm_init_list_iter( generic_iter_t *list_iter, tree_t **stack_root, long arg_size, long root_size, const ref_t *root_ref, int generic_id ) { list_iter->type = IT_Tree; list_iter->root_ref = *root_ref; list_iter->stack_root = stack_root; list_iter->yield_size = 0; list_iter->root_size = root_size; list_iter->ref.kid = 0; list_iter->ref.next = 0; list_iter->arg_size = arg_size; list_iter->generic_id = generic_id; } void colm_list_iter_destroy( program_t *prg, tree_t ***psp, generic_iter_t *iter ) { if ( (int)iter->type != 0 ) { int i; tree_t **sp = *psp; long cur_stack_size = vm_ssize() - iter->root_size; assert( iter->yield_size == cur_stack_size ); vm_popn( iter->yield_size ); for ( i = 0; i < iter->arg_size; i++ ) { //colm_tree_downref( prg, sp, vm_pop_tree() ); vm_pop_value(); } iter->type = 0; *psp = sp; } } tree_t *colm_list_iter_advance( program_t *prg, tree_t ***psp, generic_iter_t *iter ) { tree_t **sp = *psp; assert( iter->yield_size == (vm_ssize() - iter->root_size) ); if ( iter->ref.kid == 0 ) { /* kid_t is zero, start from the root. */ list_t *list = *((list_t**)iter->root_ref.kid); iter->ref.kid = (kid_t*)list->head; iter->ref.next = 0; //= iter->rootRef; //iter //iterFind( prg, psp, iter, true ); } else { /* Have a previous item, continue searching from there. */ //iterFind( prg, psp, iter, false ); list_el_t *list_el = (list_el_t*)iter->ref.kid; list_el = list_el->list_next; iter->ref.kid = (kid_t*)list_el; iter->ref.next = 0; } sp = *psp; iter->yield_size = vm_ssize() - iter->root_size; return (iter->ref.kid ? prg->true_val : prg->false_val ); } tree_t *colm_rev_list_iter_advance( program_t *prg, tree_t ***psp, generic_iter_t *iter ) { tree_t **sp = *psp; assert( iter->yield_size == (vm_ssize() - iter->root_size) ); if ( iter->ref.kid == 0 ) { /* kid_t is zero, start from the root. */ list_t *list = *((list_t**)iter->root_ref.kid); iter->ref.kid = (kid_t*)list->tail; iter->ref.next = 0; //= iter->rootRef; //iter //iterFind( prg, psp, iter, true ); } else { /* Have a previous item, continue searching from there. */ //iterFind( prg, psp, iter, false ); list_el_t *list_el = (list_el_t*)iter->ref.kid; list_el = list_el->list_prev; iter->ref.kid = (kid_t*)list_el; iter->ref.next = 0; } sp = *psp; iter->yield_size = vm_ssize() - iter->root_size; return (iter->ref.kid ? prg->true_val : prg->false_val ); } tree_t *colm_map_iter_advance( program_t *prg, tree_t ***psp, generic_iter_t *iter ) { tree_t **sp = *psp; assert( iter->yield_size == (vm_ssize() - iter->root_size) ); if ( iter->ref.kid == 0 ) { /* kid_t is zero, start from the root. */ map_t *map = *((map_t**)iter->root_ref.kid); iter->ref.kid = (kid_t*)map->head; iter->ref.next = 0; //= iter->rootRef; //iter //iterFind( prg, psp, iter, true ); } else { /* Have a previous item, continue searching from there. */ //iterFind( prg, psp, iter, false ); map_el_t *map_el = (map_el_t*)iter->ref.kid; map_el = map_el->next; iter->ref.kid = (kid_t*)map_el; iter->ref.next = 0; } sp = *psp; iter->yield_size = vm_ssize() - iter->root_size; return (iter->ref.kid ? prg->true_val : prg->false_val ); } tree_t *colm_list_iter_deref_cur( program_t *prg, generic_iter_t *iter ) { struct generic_info *gi = &prg->rtd->generic_info[iter->generic_id]; list_el_t *el = (list_el_t*)iter->ref.kid; struct colm_struct *s = el != 0 ? colm_struct_container( el, gi->el_offset ) : 0; return (tree_t*)s; } value_t colm_viter_deref_cur( program_t *prg, generic_iter_t *iter ) { struct generic_info *gi = &prg->rtd->generic_info[iter->generic_id]; list_el_t *el = (list_el_t*)iter->ref.kid; struct colm_struct *s = el != 0 ? colm_struct_container( el, gi->el_offset ) : 0; value_t value = colm_struct_get_field( s, value_t, 0 ); if ( gi->value_type == TYPE_TREE ) colm_tree_upref( prg, (tree_t*)value ); return value; } void colm_init_tree_iter( tree_iter_t *tree_iter, tree_t **stack_root, long arg_size, long root_size, const ref_t *root_ref, int search_id ) { tree_iter->type = IT_Tree; tree_iter->root_ref = *root_ref; tree_iter->search_id = search_id; tree_iter->stack_root = stack_root; tree_iter->yield_size = 0; tree_iter->root_size = root_size; tree_iter->ref.kid = 0; tree_iter->ref.next = 0; tree_iter->arg_size = arg_size; } void colm_init_rev_tree_iter( rev_tree_iter_t *rev_triter, tree_t **stack_root, long arg_size, long root_size, const ref_t *root_ref, int search_id, int children ) { rev_triter->type = IT_RevTree; rev_triter->root_ref = *root_ref; rev_triter->search_id = search_id; rev_triter->stack_root = stack_root; rev_triter->yield_size = children; rev_triter->root_size = root_size; rev_triter->kid_at_yield = 0; rev_triter->children = children; rev_triter->ref.kid = 0; rev_triter->ref.next = 0; rev_triter->arg_size = arg_size; } void init_user_iter( user_iter_t *user_iter, tree_t **stack_root, long root_size, long arg_size, long search_id ) { user_iter->type = IT_User; user_iter->stack_root = stack_root; user_iter->arg_size = arg_size; user_iter->yield_size = 0; user_iter->root_size = root_size; user_iter->resume = 0; user_iter->frame = 0; user_iter->search_id = search_id; user_iter->ref.kid = 0; user_iter->ref.next = 0; } user_iter_t *colm_uiter_create( program_t *prg, tree_t ***psp, struct function_info *fi, long search_id ) { tree_t **sp = *psp; vm_pushn( sizeof(user_iter_t) / sizeof(word_t) ); void *mem = vm_ptop(); user_iter_t *uiter = mem; tree_t **stack_root = vm_ptop(); long root_size = vm_ssize(); init_user_iter( uiter, stack_root, root_size, fi->arg_size, search_id ); *psp = sp; return uiter; } void uiter_init( program_t *prg, tree_t **sp, user_iter_t *uiter, struct function_info *fi, int revert_on ) { /* Set up the first yeild so when we resume it starts at the beginning. */ uiter->ref.kid = 0; uiter->yield_size = vm_ssize() - uiter->root_size; // uiter->frame = &uiter->stackRoot[-IFR_AA]; if ( revert_on ) uiter->resume = prg->rtd->frame_info[fi->frame_id].codeWV; else uiter->resume = prg->rtd->frame_info[fi->frame_id].codeWC; } void colm_tree_iter_destroy( program_t *prg, tree_t ***psp, tree_iter_t *iter ) { if ( (int)iter->type != 0 ) { int i; tree_t **sp = *psp; long cur_stack_size = vm_ssize() - iter->root_size; assert( iter->yield_size == cur_stack_size ); vm_popn( iter->yield_size ); for ( i = 0; i < iter->arg_size; i++ ) colm_tree_downref( prg, sp, vm_pop_tree() ); iter->type = 0; *psp = sp; } } void colm_rev_tree_iter_destroy( struct colm_program *prg, tree_t ***psp, rev_tree_iter_t *riter ) { if ( (int)riter->type != 0 ) { int i; tree_t **sp = *psp; long cur_stack_size = vm_ssize() - riter->root_size; assert( riter->yield_size == cur_stack_size ); vm_popn( riter->yield_size ); for ( i = 0; i < riter->arg_size; i++ ) colm_tree_downref( prg, sp, vm_pop_tree() ); riter->type = 0; *psp = sp; } } void colm_uiter_destroy( program_t *prg, tree_t ***psp, user_iter_t *uiter ) { if ( uiter != 0 && (int)uiter->type != 0 ) { tree_t **sp = *psp; /* We should always be coming from a yield. The current stack size will be * nonzero and the stack size in the iterator will be correct. */ long cur_stack_size = vm_ssize() - uiter->root_size; assert( uiter->yield_size == cur_stack_size ); vm_popn( uiter->yield_size ); vm_popn( sizeof(user_iter_t) / sizeof(word_t) ); uiter->type = 0; *psp = sp; } } void colm_uiter_unwind( program_t *prg, tree_t ***psp, user_iter_t *uiter ) { if ( uiter != 0 && (int)uiter->type != 0 ) { tree_t **sp = *psp; /* We should always be coming from a yield. The current stack size will be * nonzero and the stack size in the iterator will be correct. */ long cur_stack_size = vm_ssize() - uiter->root_size; assert( uiter->yield_size == cur_stack_size ); long arg_size = uiter->arg_size; vm_popn( uiter->yield_size ); vm_popn( sizeof(user_iter_t) / sizeof(word_t) ); /* The IN_PREP_ARGS stack data. */ vm_popn( arg_size ); vm_pop_value(); uiter->type = 0; *psp = sp; } } tree_t *tree_iter_deref_cur( tree_iter_t *iter ) { return iter->ref.kid == 0 ? 0 : iter->ref.kid->tree; } void set_triter_cur( program_t *prg, tree_iter_t *iter, tree_t *tree ) { iter->ref.kid->tree = tree; } void set_uiter_cur( program_t *prg, user_iter_t *uiter, tree_t *tree ) { uiter->ref.kid->tree = tree; } void split_iter_cur( program_t *prg, tree_t ***psp, tree_iter_t *iter ) { if ( iter->ref.kid == 0 ) return; split_ref( prg, psp, &iter->ref ); } void iter_find( program_t *prg, tree_t ***psp, tree_iter_t *iter, int try_first, int with_ignore ) { int any_tree = iter->search_id == prg->rtd->any_id; tree_t **top = iter->stack_root; kid_t *child; tree_t **sp = *psp; rec_call: if ( try_first && ( iter->ref.kid->tree->id == iter->search_id || any_tree ) ) { *psp = sp; return; } else { child = tree_child_maybe_ignore( prg, iter->ref.kid->tree, with_ignore ); if ( child != 0 ) { vm_contiguous( 2 ); vm_push_ref( iter->ref.next ); vm_push_kid( iter->ref.kid ); iter->ref.kid = child; iter->ref.next = (ref_t*)vm_ptop(); while ( iter->ref.kid != 0 ) { try_first = true; goto rec_call; rec_return: iter->ref.kid = iter->ref.kid->next; } iter->ref.kid = vm_pop_kid(); iter->ref.next = vm_pop_ref(); } } if ( top != vm_ptop() ) goto rec_return; iter->ref.kid = 0; *psp = sp; } tree_t *tree_iter_advance( program_t *prg, tree_t ***psp, tree_iter_t *iter, int with_ignore ) { tree_t **sp = *psp; assert( iter->yield_size == (vm_ssize() - iter->root_size) ); if ( iter->ref.kid == 0 ) { /* kid_t is zero, start from the root. */ iter->ref = iter->root_ref; iter_find( prg, psp, iter, true, with_ignore ); } else { /* Have a previous item, continue searching from there. */ iter_find( prg, psp, iter, false, with_ignore ); } sp = *psp; iter->yield_size = vm_ssize() - iter->root_size; return (iter->ref.kid ? prg->true_val : prg->false_val ); } tree_t *tree_iter_next_child( program_t *prg, tree_t ***psp, tree_iter_t *iter ) { tree_t **sp = *psp; assert( iter->yield_size == (vm_ssize() - iter->root_size) ); kid_t *kid = 0; if ( iter->ref.kid == 0 ) { /* kid_t is zero, start from the first child. */ kid_t *child = tree_child( prg, iter->root_ref.kid->tree ); if ( child == 0 ) iter->ref.next = 0; else { /* Make a reference to the root. */ vm_contiguous( 2 ); vm_push_ref( iter->root_ref.next ); vm_push_kid( iter->root_ref.kid ); iter->ref.next = (ref_t*)vm_ptop(); kid = child; } } else { /* Start at next. */ kid = iter->ref.kid->next; } if ( iter->search_id != prg->rtd->any_id ) { /* Have a previous item, go to the next sibling. */ while ( kid != 0 && kid->tree->id != iter->search_id ) kid = kid->next; } iter->ref.kid = kid; iter->yield_size = vm_ssize() - iter->root_size; *psp = sp; return ( iter->ref.kid ? prg->true_val : prg->false_val ); } tree_t *tree_rev_iter_prev_child( program_t *prg, tree_t ***psp, rev_tree_iter_t *iter ) { tree_t **sp = *psp; assert( iter->yield_size == ( vm_ssize() - iter->root_size ) ); if ( iter->kid_at_yield != iter->ref.kid ) { /* Need to reload the kids. */ vm_popn( iter->children ); int c; kid_t *kid = tree_child( prg, iter->root_ref.kid->tree ); for ( c = 0; c < iter->children; c++ ) { vm_push_kid( kid ); kid = kid->next; } } if ( iter->ref.kid != 0 ) { vm_pop_ignore(); iter->children -= 1; } if ( iter->search_id != prg->rtd->any_id ) { /* Have a previous item, go to the next sibling. */ while ( iter->children > 0 && ((kid_t*)(vm_top()))->tree->id != iter->search_id ) { iter->children -= 1; vm_pop_ignore(); } } if ( iter->children == 0 ) { iter->ref.next = 0; iter->ref.kid = 0; } else { iter->ref.next = &iter->root_ref; iter->ref.kid = (kid_t*)vm_top(); } /* We will use this to detect a split above the iterated tree. */ iter->kid_at_yield = iter->ref.kid; iter->yield_size = vm_ssize() - iter->root_size; *psp = sp; return (iter->ref.kid ? prg->true_val : prg->false_val ); } void iter_find_repeat( program_t *prg, tree_t ***psp, tree_iter_t *iter, int try_first ) { tree_t **sp = *psp; int any_tree = iter->search_id == prg->rtd->any_id; tree_t **top = iter->stack_root; kid_t *child; rec_call: if ( try_first && ( iter->ref.kid->tree->id == iter->search_id || any_tree ) ) { *psp = sp; return; } else { /* The repeat iterator is just like the normal top-down-left-right, * execept it only goes into the children of a node if the node is the * root of the iteration, or if does not have any neighbours to the * right. */ if ( top == vm_ptop() || iter->ref.kid->next == 0 ) { child = tree_child( prg, iter->ref.kid->tree ); if ( child != 0 ) { vm_contiguous( 2 ); vm_push_ref( iter->ref.next ); vm_push_kid( iter->ref.kid ); iter->ref.kid = child; iter->ref.next = (ref_t*)vm_ptop(); while ( iter->ref.kid != 0 ) { try_first = true; goto rec_call; rec_return: iter->ref.kid = iter->ref.kid->next; } iter->ref.kid = vm_pop_kid(); iter->ref.next = vm_pop_ref(); } } } if ( top != vm_ptop() ) goto rec_return; iter->ref.kid = 0; *psp = sp; } tree_t *tree_iter_next_repeat( program_t *prg, tree_t ***psp, tree_iter_t *iter ) { tree_t **sp = *psp; assert( iter->yield_size == ( vm_ssize() - iter->root_size ) ); if ( iter->ref.kid == 0 ) { /* kid_t is zero, start from the root. */ iter->ref = iter->root_ref; iter_find_repeat( prg, psp, iter, true ); } else { /* Have a previous item, continue searching from there. */ iter_find_repeat( prg, psp, iter, false ); } sp = *psp; iter->yield_size = vm_ssize() - iter->root_size; return (iter->ref.kid ? prg->true_val : prg->false_val ); } void iter_find_rev_repeat( program_t *prg, tree_t ***psp, tree_iter_t *iter, int try_first ) { tree_t **sp = *psp; int any_tree = iter->search_id == prg->rtd->any_id; tree_t **top = iter->stack_root; kid_t *child; if ( try_first ) { while ( true ) { if ( top == vm_ptop() || iter->ref.kid->next == 0 ) { child = tree_child( prg, iter->ref.kid->tree ); if ( child == 0 ) break; vm_contiguous( 2 ); vm_push_ref( iter->ref.next ); vm_push_kid( iter->ref.kid ); iter->ref.kid = child; iter->ref.next = (ref_t*)vm_ptop(); } else { /* Not the top and not there is a next, go over to it. */ iter->ref.kid = iter->ref.kid->next; } } goto first; } while ( true ) { if ( top == vm_ptop() ) { iter->ref.kid = 0; return; } if ( iter->ref.kid->next == 0 ) { /* Go up one and then down. Remember we can't use iter->ref.next * because the chain may have been split, setting it null (to * prevent repeated walks up). */ ref_t *ref = (ref_t*)vm_ptop(); iter->ref.kid = tree_child( prg, ref->kid->tree ); } else { iter->ref.kid = vm_pop_kid(); iter->ref.next = vm_pop_ref(); } first: if ( iter->ref.kid->tree->id == iter->search_id || any_tree ) { *psp = sp; return; } } *psp = sp; return; } tree_t *tree_iter_prev_repeat( program_t *prg, tree_t ***psp, tree_iter_t *iter ) { tree_t **sp = *psp; assert( iter->yield_size == (vm_ssize() - iter->root_size) ); if ( iter->ref.kid == 0 ) { /* kid_t is zero, start from the root. */ iter->ref = iter->root_ref; iter_find_rev_repeat( prg, psp, iter, true ); } else { /* Have a previous item, continue searching from there. */ iter_find_rev_repeat( prg, psp, iter, false ); } sp = *psp; iter->yield_size = vm_ssize() - iter->root_size; return (iter->ref.kid ? prg->true_val : prg->false_val ); }