diff options
Diffstat (limited to 'src/tree.c')
-rw-r--r-- | src/tree.c | 2263 |
1 files changed, 2263 insertions, 0 deletions
diff --git a/src/tree.c b/src/tree.c new file mode 100644 index 0000000..1abb720 --- /dev/null +++ b/src/tree.c @@ -0,0 +1,2263 @@ +/* + * Copyright 2007-2012 Adrian Thurston <thurston@complang.org> + */ + +/* This file is part of Colm. + * + * Colm 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. + * + * Colm 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 Colm; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <colm/pdarun.h> +#include <colm/tree.h> +#include <colm/pool.h> +#include <colm/bytecode.h> +#include <colm/debug.h> +#include <colm/map.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> + +#define true 1 +#define false 0 + +#define BUFFER_INITIAL_SIZE 4096 + +void listPrepend( List *list, ListEl *new_el) { listAddBefore(list, list->head, new_el); } +void listAppend( List *list, ListEl *new_el) { listAddAfter(list, list->tail, new_el); } + +ListEl *listDetach( List *list, ListEl *el ); +ListEl *listDetachFirst(List *list ) { return listDetach(list, list->head); } +ListEl *listDetachLast(List *list ) { return listDetach(list, list->tail); } + +long listLength(List *list) + { return list->listLen; } + +Kid *allocAttrs( Program *prg, long length ) +{ + Kid *cur = 0; + long i; + for ( i = 0; i < length; i++ ) { + Kid *next = cur; + cur = kidAllocate( prg ); + cur->next = next; + } + return cur; +} + +void freeAttrs( Program *prg, Kid *attrs ) +{ + Kid *cur = attrs; + while ( cur != 0 ) { + Kid *next = cur->next; + kidFree( prg, cur ); + cur = next; + } +} + +void freeKidList( Program *prg, Kid *kid ) +{ + while ( kid != 0 ) { + Kid *next = kid->next; + kidFree( prg, kid ); + kid = next; + } +} + +void setAttr( Tree *tree, long pos, Tree *val ) +{ + long i; + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + for ( i = 0; i < pos; i++ ) + kid = kid->next; + kid->tree = val; +} + +Tree *colm_get_global( Program *prg, long pos ) + { return colm_get_attr( prg->global, pos ); } + +Tree *colm_get_attr( Tree *tree, long pos ) +{ + long i; + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + for ( i = 0; i < pos; i++ ) + kid = kid->next; + return kid->tree; +} + + +Tree *colm_get_repeat_next( Tree *tree ) +{ + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + return kid->next->tree; +} + +Tree *colm_get_repeat_val( Tree *tree ) +{ + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + return kid->tree; +} + +int colm_repeat_end( Tree *tree ) +{ + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + return kid == 0; +} + +int colm_list_last( Tree *tree ) +{ + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + return kid->next == 0; +} + +Kid *getAttrKid( Tree *tree, long pos ) +{ + long i; + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + for ( i = 0; i < pos; i++ ) + kid = kid->next; + return kid; +} + +Kid *kidListConcat( Kid *list1, Kid *list2 ) +{ + if ( list1 == 0 ) + return list2; + else if ( list2 == 0 ) + return list1; + + Kid *dest = list1; + while ( dest->next != 0 ) + dest = dest->next; + dest->next = list2; + return list1; +} + + +Stream *openStreamFile( Program *prg, char *name, FILE *file ) +{ + Stream *res = (Stream*)mapElAllocate( prg ); + res->id = LEL_ID_STREAM; + res->in = newSourceStreamFile( name, file ); + return res; +} + +Stream *openStreamFd( Program *prg, char *name, long fd ) +{ + Stream *res = (Stream*)mapElAllocate( prg ); + res->id = LEL_ID_STREAM; + res->in = newSourceStreamFd( name, fd ); + return res; +} + +Stream *openFile( Program *prg, Tree *name, Tree *mode ) +{ + Head *headName = ((Str*)name)->value; + Head *headMode = ((Str*)mode)->value; + Stream *stream = 0; + + const char *givenMode = stringData(headMode); + const char *fopenMode = 0; + if ( memcmp( givenMode, "r", stringLength(headMode) ) == 0 ) + fopenMode = "rb"; + else if ( memcmp( givenMode, "w", stringLength(headMode) ) == 0 ) + fopenMode = "wb"; + else { + fatal( "unknown file open mode: %s\n", givenMode ); + } + + /* Need to make a C-string (null terminated). */ + char *fileName = (char*)malloc(stringLength(headName)+1); + memcpy( fileName, stringData(headName), stringLength(headName) ); + fileName[stringLength(headName)] = 0; + FILE *file = fopen( fileName, fopenMode ); + if ( file != 0 ) + stream = openStreamFile( prg, fileName, file ); + + return stream; +} + +Tree *constructInteger( Program *prg, long i ) +{ + Int *integer = (Int*) treeAllocate( prg ); + integer->id = LEL_ID_INT; + integer->value = i; + + return (Tree*)integer; +} + +Tree *constructString( Program *prg, Head *s ) +{ + Str *str = (Str*) treeAllocate( prg ); + str->id = LEL_ID_STR; + str->value = s; + + return (Tree*)str; +} + +Tree *constructPointer( Program *prg, Tree *tree ) +{ + Kid *kid = kidAllocate( prg ); + kid->tree = tree; + kid->next = prg->heap; + prg->heap = kid; + + Pointer *pointer = (Pointer*) treeAllocate( prg ); + pointer->id = LEL_ID_PTR; + pointer->value = kid; + + return (Tree*)pointer; +} + +Tree *constructTerm( Program *prg, Word id, Head *tokdata ) +{ + LangElInfo *lelInfo = prg->rtd->lelInfo; + + Tree *tree = treeAllocate( prg ); + tree->id = id; + tree->refs = 0; + tree->tokdata = tokdata; + + int objectLength = lelInfo[tree->id].objectLength; + tree->child = allocAttrs( prg, objectLength ); + + return tree; +} + +Tree *constructStream( Program *prg ) +{ + Stream *input = streamAllocate( prg ); + input->refs = 0; + input->id = LEL_ID_STREAM; + + input->in = newSourceStreamGeneric( "<internal>" ); + return (Tree*)input; +} + +Kid *constructReplacementKid( Tree **bindings, Program *prg, Kid *prev, long pat ); + +static Kid *constructIgnoreList( Program *prg, long ignoreInd ) +{ + PatConsNode *nodes = prg->rtd->patReplNodes; + + Kid *first = 0, *last = 0; + while ( ignoreInd >= 0 ) { + Head *ignoreData = stringAllocPointer( prg, nodes[ignoreInd].data, nodes[ignoreInd].length ); + + Tree *ignTree = treeAllocate( prg ); + ignTree->refs = 1; + ignTree->id = nodes[ignoreInd].id; + ignTree->tokdata = ignoreData; + + Kid *ignKid = kidAllocate( prg ); + ignKid->tree = ignTree; + ignKid->next = 0; + + if ( last == 0 ) + first = ignKid; + else + last->next = ignKid; + + ignoreInd = nodes[ignoreInd].next; + last = ignKid; + } + + return first; +} + +static Kid *constructLeftIgnoreList( Program *prg, long pat ) +{ + PatConsNode *nodes = prg->rtd->patReplNodes; + return constructIgnoreList( prg, nodes[pat].leftIgnore ); +} + +static Kid *constructRightIgnoreList( Program *prg, long pat ) +{ + PatConsNode *nodes = prg->rtd->patReplNodes; + return constructIgnoreList( prg, nodes[pat].rightIgnore ); +} + +static void insLeftIgnore( Program *prg, Tree *tree, Tree *ignoreList ) +{ + assert( ! (tree->flags & AF_LEFT_IGNORE) ); + + /* Allocate. */ + Kid *kid = kidAllocate( prg ); + kid->tree = ignoreList; + treeUpref( ignoreList ); + + /* Attach it. */ + kid->next = tree->child; + tree->child = kid; + + tree->flags |= AF_LEFT_IGNORE; +} + +static void insRightIgnore( Program *prg, Tree *tree, Tree *ignoreList ) +{ + assert( ! (tree->flags & AF_RIGHT_IGNORE) ); + + /* Insert an ignore head in the child list. */ + Kid *kid = kidAllocate( prg ); + kid->tree = ignoreList; + treeUpref( ignoreList ); + + /* Attach it. */ + if ( tree->flags & AF_LEFT_IGNORE ) { + kid->next = tree->child->next; + tree->child->next = kid; + } + else { + kid->next = tree->child; + tree->child = kid; + } + + tree->flags |= AF_RIGHT_IGNORE; +} + +Tree *pushRightIgnore( Program *prg, Tree *pushTo, Tree *rightIgnore ) +{ + /* About to alter the data tree. Split first. */ + pushTo = splitTree( prg, pushTo ); + + if ( pushTo->flags & AF_RIGHT_IGNORE ) { + /* The previous token already has a right ignore. Merge by + * attaching it as a left ignore of the new list. */ + Kid *curIgnore = treeRightIgnoreKid( prg, pushTo ); + insLeftIgnore( prg, rightIgnore, curIgnore->tree ); + + /* Replace the current ignore. Safe to access refs here because we just + * upreffed it in insLeftIgnore. */ + curIgnore->tree->refs -= 1; + curIgnore->tree = rightIgnore; + treeUpref( rightIgnore ); + } + else { + /* Attach The ignore list. */ + insRightIgnore( prg, pushTo, rightIgnore ); + } + + return pushTo; +} + +Tree *pushLeftIgnore( Program *prg, Tree *pushTo, Tree *leftIgnore ) +{ + pushTo = splitTree( prg, pushTo ); + + /* Attach as left ignore to the token we are sending. */ + if ( pushTo->flags & AF_LEFT_IGNORE ) { + /* The token already has a left-ignore. Merge by attaching it as a + * right ignore of the new list. */ + Kid *curIgnore = treeLeftIgnoreKid( prg, pushTo ); + insRightIgnore( prg, leftIgnore, curIgnore->tree ); + + /* Replace the current ignore. Safe to upref here because we just + * upreffed it in insRightIgnore. */ + curIgnore->tree->refs -= 1; + curIgnore->tree = leftIgnore; + treeUpref( leftIgnore ); + } + else { + /* Attach the ignore list. */ + insLeftIgnore( prg, pushTo, leftIgnore ); + } + + return pushTo; +} + +static void remLeftIgnore( Program *prg, Tree **sp, Tree *tree ) +{ + assert( tree->flags & AF_LEFT_IGNORE ); + + Kid *next = tree->child->next; + treeDownref( prg, sp, tree->child->tree ); + kidFree( prg, tree->child ); + tree->child = next; + + tree->flags &= ~AF_LEFT_IGNORE; +} + +static void remRightIgnore( Program *prg, Tree **sp, Tree *tree ) +{ + assert( tree->flags & AF_RIGHT_IGNORE ); + + if ( tree->flags & AF_LEFT_IGNORE ) { + Kid *next = tree->child->next->next; + treeDownref( prg, sp, tree->child->next->tree ); + kidFree( prg, tree->child->next ); + tree->child->next = next; + } + else { + Kid *next = tree->child->next; + treeDownref( prg, sp, tree->child->tree ); + kidFree( prg, tree->child ); + tree->child = next; + } + + tree->flags &= ~AF_RIGHT_IGNORE; +} + +Tree *popRightIgnore( Program *prg, Tree **sp, Tree *popFrom, Tree **rightIgnore ) +{ + /* Modifying the tree we are detaching from. */ + popFrom = splitTree( prg, popFrom ); + + Kid *riKid = treeRightIgnoreKid( prg, popFrom ); + + /* If the right ignore has a left ignore, then that was the original + * right ignore. */ + Kid *li = treeLeftIgnoreKid( prg, riKid->tree ); + if ( li != 0 ) { + treeUpref( li->tree ); + remLeftIgnore( prg, sp, riKid->tree ); + *rightIgnore = riKid->tree; + treeUpref( *rightIgnore ); + riKid->tree = li->tree; + } + else { + *rightIgnore = riKid->tree; + treeUpref( *rightIgnore ); + remRightIgnore( prg, sp, popFrom ); + } + + return popFrom; +} + +Tree *popLeftIgnore( Program *prg, Tree **sp, Tree *popFrom, Tree **leftIgnore ) +{ + /* Modifying, make the write safe. */ + popFrom = splitTree( prg, popFrom ); + + Kid *liKid = treeLeftIgnoreKid( prg, popFrom ); + + /* If the left ignore has a right ignore, then that was the original + * left ignore. */ + Kid *ri = treeRightIgnoreKid( prg, liKid->tree ); + if ( ri != 0 ) { + treeUpref( ri->tree ); + remRightIgnore( prg, sp, liKid->tree ); + *leftIgnore = liKid->tree; + treeUpref( *leftIgnore ); + liKid->tree = ri->tree; + } + else { + *leftIgnore = liKid->tree; + treeUpref( *leftIgnore ); + remLeftIgnore( prg, sp, popFrom ); + } + + return popFrom; +} + + +/* Returns an uprefed tree. Saves us having to downref and bindings to zero to + * return a zero-ref tree. */ +Tree *constructReplacementTree( Kid *kid, Tree **bindings, Program *prg, long pat ) +{ + PatConsNode *nodes = prg->rtd->patReplNodes; + LangElInfo *lelInfo = prg->rtd->lelInfo; + Tree *tree = 0; + + if ( nodes[pat].bindId > 0 ) { + /* All bindings have been uprefed. */ + tree = bindings[nodes[pat].bindId]; + + long ignore = nodes[pat].leftIgnore; + Tree *leftIgnore = 0; + if ( ignore >= 0 ) { + Kid *ignore = constructLeftIgnoreList( prg, pat ); + + leftIgnore = treeAllocate( prg ); + leftIgnore->id = LEL_ID_IGNORE; + leftIgnore->child = ignore; + + tree = pushLeftIgnore( prg, tree, leftIgnore ); + } + + ignore = nodes[pat].rightIgnore; + Tree *rightIgnore = 0; + if ( ignore >= 0 ) { + Kid *ignore = constructRightIgnoreList( prg, pat ); + + rightIgnore = treeAllocate( prg ); + rightIgnore->id = LEL_ID_IGNORE; + rightIgnore->child = ignore; + + tree = pushRightIgnore( prg, tree, rightIgnore ); + } + } + else { + tree = treeAllocate( prg ); + tree->id = nodes[pat].id; + tree->refs = 1; + tree->tokdata = nodes[pat].length == 0 ? 0 : + stringAllocPointer( prg, + nodes[pat].data, nodes[pat].length ); + tree->prodNum = nodes[pat].prodNum; + + int objectLength = lelInfo[tree->id].objectLength; + + Kid *attrs = allocAttrs( prg, objectLength ); + Kid *child = constructReplacementKid( bindings, prg, + 0, nodes[pat].child ); + + tree->child = kidListConcat( attrs, child ); + + /* Right first, then left. */ + Kid *ignore = constructRightIgnoreList( prg, pat ); + if ( ignore != 0 ) { + Tree *ignoreList = treeAllocate( prg ); + ignoreList->id = LEL_ID_IGNORE; + ignoreList->refs = 1; + ignoreList->child = ignore; + + Kid *ignoreHead = kidAllocate( prg ); + ignoreHead->tree = ignoreList; + ignoreHead->next = tree->child; + tree->child = ignoreHead; + + tree->flags |= AF_RIGHT_IGNORE; + } + + ignore = constructLeftIgnoreList( prg, pat ); + if ( ignore != 0 ) { + Tree *ignoreList = treeAllocate( prg ); + ignoreList->id = LEL_ID_IGNORE; + ignoreList->refs = 1; + ignoreList->child = ignore; + + Kid *ignoreHead = kidAllocate( prg ); + ignoreHead->tree = ignoreList; + ignoreHead->next = tree->child; + tree->child = ignoreHead; + + tree->flags |= AF_LEFT_IGNORE; + } + + int i; + for ( i = 0; i < lelInfo[tree->id].numCaptureAttr; i++ ) { + long ci = pat+1+i; + CaptureAttr *ca = prg->rtd->captureAttr + lelInfo[tree->id].captureAttr + i; + Tree *attr = treeAllocate( prg ); + attr->id = nodes[ci].id; + attr->refs = 1; + attr->tokdata = nodes[ci].length == 0 ? 0 : + stringAllocPointer( prg, + nodes[ci].data, nodes[ci].length ); + + setAttr( tree, ca->offset, attr ); + } + } + + return tree; +} + +Kid *constructReplacementKid( Tree **bindings, Program *prg, Kid *prev, long pat ) +{ + PatConsNode *nodes = prg->rtd->patReplNodes; + Kid *kid = 0; + + if ( pat != -1 ) { + kid = kidAllocate( prg ); + kid->tree = constructReplacementTree( kid, bindings, prg, pat ); + + /* Recurse down next. */ + Kid *next = constructReplacementKid( bindings, prg, + kid, nodes[pat].next ); + + kid->next = next; + } + + return kid; +} + +Tree *constructToken( Program *prg, Tree **args, long nargs ) +{ + Int *idInt = (Int*)args[0]; + Str *textStr = (Str*)args[1]; + + long id = idInt->value; + Head *tokdata = stringCopy( prg, textStr->value ); + + LangElInfo *lelInfo = prg->rtd->lelInfo; + Tree *tree; + + if ( lelInfo[id].ignore ) { + tree = treeAllocate( prg ); + tree->refs = 1; + tree->id = id; + tree->tokdata = tokdata; + } + else { + long objectLength = lelInfo[id].objectLength; + assert( nargs-2 <= objectLength ); + + Kid *attrs = allocAttrs( prg, objectLength ); + + tree = treeAllocate( prg ); + tree->id = id; + tree->refs = 1; + tree->tokdata = tokdata; + + tree->child = attrs; + + long i; + for ( i = 2; i < nargs; i++ ) { + setAttr( tree, i-2, args[i] ); + treeUpref( colm_get_attr( tree, i-2 ) ); + } + } + return tree; +} + +Tree *castTree( Program *prg, int langElId, Tree *tree ) +{ + LangElInfo *lelInfo = prg->rtd->lelInfo; + + /* Need to keep a lookout for next down. If + * copying it, return the copy. */ + Tree *newTree = treeAllocate( prg ); + + newTree->id = langElId; + newTree->tokdata = stringCopy( prg, tree->tokdata ); + + /* Invalidate the production number. */ + newTree->prodNum = -1; + + /* Copy the child list. Start with ignores, then the list. */ + Kid *child = tree->child, *last = 0; + + /* Flags we are interested in. */ + newTree->flags |= tree->flags & ( AF_LEFT_IGNORE | AF_RIGHT_IGNORE ); + + int ignores = 0; + if ( tree->flags & AF_LEFT_IGNORE ) + ignores += 1; + if ( tree->flags & AF_RIGHT_IGNORE ) + ignores += 1; + + /* Igores. */ + while ( ignores-- > 0 ) { + Kid *newKid = kidAllocate( prg ); + + newKid->tree = child->tree; + newKid->next = 0; + newKid->tree->refs += 1; + + /* Store the first child. */ + if ( last == 0 ) + newTree->child = newKid; + else + last->next = newKid; + + child = child->next; + last = newKid; + } + + /* Skip over the source's attributes. */ + int objectLength = lelInfo[tree->id].objectLength; + while ( objectLength-- > 0 ) + child = child->next; + + /* Allocate the target type's kids. */ + objectLength = lelInfo[langElId].objectLength; + while ( objectLength-- > 0 ) { + Kid *newKid = kidAllocate( prg ); + + newKid->tree = 0; + newKid->next = 0; + + /* Store the first child. */ + if ( last == 0 ) + newTree->child = newKid; + else + last->next = newKid; + + last = newKid; + } + + /* Copy the source's children. */ + while ( child != 0 ) { + Kid *newKid = kidAllocate( prg ); + + newKid->tree = child->tree; + newKid->next = 0; + newKid->tree->refs += 1; + + /* Store the first child. */ + if ( last == 0 ) + newTree->child = newKid; + else + last->next = newKid; + + child = child->next; + last = newKid; + } + + return newTree; +} + +Tree *makeTree( Program *prg, Tree **args, long nargs ) +{ + Int *idInt = (Int*)args[0]; + + long id = idInt->value; + LangElInfo *lelInfo = prg->rtd->lelInfo; + + Tree *tree = treeAllocate( prg ); + tree->id = id; + tree->refs = 1; + + long objectLength = lelInfo[id].objectLength; + Kid *attrs = allocAttrs( prg, objectLength ); + + Kid *last = 0, *child = 0; + for ( id = 1; id < nargs; id++ ) { + Kid *kid = kidAllocate( prg ); + kid->tree = args[id]; + treeUpref( kid->tree ); + + if ( last == 0 ) + child = kid; + else + last->next = kid; + + last = kid; + } + + tree->child = kidListConcat( attrs, child ); + + return tree; +} + +int testFalse( Program *prg, Tree *tree ) +{ + int flse = ( + tree == 0 || + tree == prg->falseVal || + ( tree->id == LEL_ID_INT && ((Int*)tree)->value == 0 ) ); + return flse; +} + +Kid *copyIgnoreList( Program *prg, Kid *ignoreHeader ) +{ + Kid *newHeader = kidAllocate( prg ); + Kid *last = 0, *ic = (Kid*)ignoreHeader->tree; + while ( ic != 0 ) { + Kid *newIc = kidAllocate( prg ); + + newIc->tree = ic->tree; + newIc->tree->refs += 1; + + /* List pointers. */ + if ( last == 0 ) + newHeader->tree = (Tree*)newIc; + else + last->next = newIc; + + ic = ic->next; + last = newIc; + } + return newHeader; +} + +Kid *copyKidList( Program *prg, Kid *kidList ) +{ + Kid *newList = 0, *last = 0, *ic = kidList; + + while ( ic != 0 ) { + Kid *newIc = kidAllocate( prg ); + + newIc->tree = ic->tree; + treeUpref( newIc->tree ); + + /* List pointers. */ + if ( last == 0 ) + newList = newIc; + else + last->next = newIc; + + ic = ic->next; + last = newIc; + } + return newList; +} + +/* New tree has zero ref. */ +Tree *copyRealTree( Program *prg, Tree *tree, Kid *oldNextDown, Kid **newNextDown ) +{ + /* Need to keep a lookout for next down. If + * copying it, return the copy. */ + Tree *newTree = treeAllocate( prg ); + + newTree->id = tree->id; + newTree->tokdata = stringCopy( prg, tree->tokdata ); + newTree->prodNum = tree->prodNum; + + /* Copy the child list. Start with ignores, then the list. */ + Kid *child = tree->child, *last = 0; + + /* Left ignores. */ + if ( tree->flags & AF_LEFT_IGNORE ) { + newTree->flags |= AF_LEFT_IGNORE; +// Kid *newHeader = copyIgnoreList( prg, child ); +// +// /* Always the head. */ +// newTree->child = newHeader; +// +// child = child->next; +// last = newHeader; + } + + /* Right ignores. */ + if ( tree->flags & AF_RIGHT_IGNORE ) { + newTree->flags |= AF_RIGHT_IGNORE; +// Kid *newHeader = copyIgnoreList( prg, child ); +// if ( last == 0 ) +// newTree->child = newHeader; +// else +// last->next = newHeader; +// child = child->next; +// last = newHeader; + } + + /* Attributes and children. */ + while ( child != 0 ) { + Kid *newKid = kidAllocate( prg ); + + /* Watch out for next down. */ + if ( child == oldNextDown ) + *newNextDown = newKid; + + newKid->tree = child->tree; + newKid->next = 0; + + /* May be an attribute. */ + if ( newKid->tree != 0 ) + newKid->tree->refs += 1; + + /* Store the first child. */ + if ( last == 0 ) + newTree->child = newKid; + else + last->next = newKid; + + child = child->next; + last = newKid; + } + + return newTree; +} + +List *copyList( Program *prg, List *list, Kid *oldNextDown, Kid **newNextDown ) +{ + /* Not a need copy. */ + List *newList = (List*)mapElAllocate( prg ); + newList->id = list->genericInfo->langElId; + newList->genericInfo = list->genericInfo; + + ListEl *src = list->head; + while( src != 0 ) { + ListEl *newEl = listElAllocate( prg ); + newEl->value = src->value; + treeUpref( newEl->value ); + + listAppend( newList, newEl ); + + /* Watch out for next down. */ + if ( (Kid*)src == oldNextDown ) + *newNextDown = (Kid*)newEl; + + src = src->next; + } + + return newList; +} + +Map *copyMap( Program *prg, Map *map, Kid *oldNextDown, Kid **newNextDown ) +{ + Map *newMap = (Map*)mapElAllocate( prg ); + newMap->id = map->genericInfo->langElId; + newMap->genericInfo = map->genericInfo; + newMap->treeSize = map->treeSize; + newMap->root = 0; + + /* If there is a root, copy the tree. */ + if ( map->root != 0 ) { + newMap->root = mapCopyBranch( prg, newMap, map->root, + oldNextDown, newNextDown ); + } + MapEl *el; + for ( el = newMap->head; el != 0; el = el->next ) { + assert( map->genericInfo->typeArg == TYPE_TREE ); + treeUpref( el->tree ); + } + + return newMap; +} + +Tree *copyTree( Program *prg, Tree *tree, Kid *oldNextDown, Kid **newNextDown ) +{ + LangElInfo *lelInfo = prg->rtd->lelInfo; + long genericId = lelInfo[tree->id].genericId; + if ( genericId > 0 ) { + GenericInfo *generic = &prg->rtd->genericInfo[genericId]; + if ( generic->type == GEN_LIST ) + tree = (Tree*) copyList( prg, (List*) tree, oldNextDown, newNextDown ); + else if ( generic->type == GEN_MAP ) + tree = (Tree*) copyMap( prg, (Map*) tree, oldNextDown, newNextDown ); + else if ( generic->type == GEN_PARSER ) { + /* Need to figure out the semantics here. */ + fatal( "ATTEMPT TO COPY PARSER\n" ); + assert(false); + } + } + else if ( tree->id == LEL_ID_PTR ) + assert(false); + else if ( tree->id == LEL_ID_BOOL ) + assert(false); + else if ( tree->id == LEL_ID_INT ) + assert(false); + else if ( tree->id == LEL_ID_STR ) + assert(false); + else if ( tree->id == LEL_ID_STREAM ) + assert(false); + else { + tree = copyRealTree( prg, tree, oldNextDown, newNextDown ); + } + + assert( tree->refs == 0 ); + return tree; +} + +Tree *splitTree( Program *prg, Tree *tree ) +{ + if ( tree != 0 ) { + assert( tree->refs >= 1 ); + + if ( tree->refs > 1 ) { + Kid *oldNextDown = 0, *newNextDown = 0; + Tree *newTree = copyTree( prg, tree, oldNextDown, &newNextDown ); + treeUpref( newTree ); + + /* Downref the original. Don't need to consider freeing because + * refs were > 1. */ + tree->refs -= 1; + + tree = newTree; + } + + assert( tree->refs == 1 ); + } + return tree; +} + +Tree *createGeneric( Program *prg, long genericId ) +{ + GenericInfo *genericInfo = &prg->rtd->genericInfo[genericId]; + Tree *newGeneric = 0; + switch ( genericInfo->type ) { + case GEN_MAP: { + Map *map = (Map*)mapElAllocate( prg ); + map->id = genericInfo->langElId; + map->genericInfo = genericInfo; + newGeneric = (Tree*) map; + break; + } + case GEN_LIST: { + List *list = (List*)mapElAllocate( prg ); + list->id = genericInfo->langElId; + list->genericInfo = genericInfo; + newGeneric = (Tree*) list; + break; + } + case GEN_PARSER: { + Parser *parser = (Parser*)mapElAllocate( prg ); + parser->id = genericInfo->langElId; + parser->genericInfo = genericInfo; + parser->pdaRun = malloc( sizeof(PdaRun) ); + + /* Start off the parsing process. */ + initPdaRun( prg, parser->pdaRun, prg->rtd->pdaTables, + genericInfo->parserId, false, false, 0 ); + + newGeneric = (Tree*) parser; + break; + } + default: + assert(false); + return 0; + } + + return newGeneric; +} + + +/* We can't make recursive calls here since the tree we are freeing may be + * very large. Need the VM stack. */ +void treeFreeRec( Program *prg, Tree **sp, Tree *tree ) +{ + Tree **top = vm_ptop(); + LangElInfo *lelInfo; + long genericId; + +free_tree: + lelInfo = prg->rtd->lelInfo; + genericId = lelInfo[tree->id].genericId; + if ( genericId > 0 ) { + GenericInfo *generic = &prg->rtd->genericInfo[genericId]; + if ( generic->type == GEN_LIST ) { + List *list = (List*) tree; + ListEl *el = list->head; + while ( el != 0 ) { + ListEl *next = el->next; + vm_push( el->value ); + listElFree( prg, el ); + el = next; + } + mapElFree( prg, (MapEl*)list ); + } + else if ( generic->type == GEN_MAP ) { + Map *map = (Map*)tree; + MapEl *el = map->head; + while ( el != 0 ) { + MapEl *next = el->next; + vm_push( el->key ); + vm_push( el->tree ); + mapElFree( prg, el ); + el = next; + } + mapElFree( prg, (MapEl*)map ); + } + else if ( generic->type == GEN_PARSER ) { + Parser *parser = (Parser*)tree; + clearPdaRun( prg, sp, parser->pdaRun ); + free( parser->pdaRun ); + treeDownref( prg, sp, (Tree*)parser->input ); + mapElFree( prg, (MapEl*)parser ); + } + else { + assert(false); + } + } + else { + if ( tree->id == LEL_ID_STR ) { + Str *str = (Str*) tree; + stringFree( prg, str->value ); + treeFree( prg, tree ); + } + else if ( tree->id == LEL_ID_BOOL || tree->id == LEL_ID_INT ) + treeFree( prg, tree ); + else if ( tree->id == LEL_ID_PTR ) + treeFree( prg, tree ); + else if ( tree->id == LEL_ID_STREAM ) { + Stream *stream = (Stream*)tree; + clearSourceStream( prg, sp, stream->in ); + if ( stream->in->file != 0 ) + fclose( stream->in->file ); + else if ( stream->in->fd > 0 ) + close( stream->in->fd ); + free( stream->in ); + streamFree( prg, stream ); + } + else { + if ( tree->id != LEL_ID_IGNORE ) + stringFree( prg, tree->tokdata ); + + /* Attributes and grammar-based children. */ + Kid *child = tree->child; + while ( child != 0 ) { + Kid *next = child->next; + vm_push( child->tree ); + kidFree( prg, child ); + child = next; + } + + treeFree( prg, tree ); + } + } + + /* Any trees to downref? */ + while ( sp != top ) { + tree = vm_pop(); + if ( tree != 0 ) { + assert( tree->refs > 0 ); + tree->refs -= 1; + if ( tree->refs == 0 ) + goto free_tree; + } + } +} + +void treeUpref( Tree *tree ) +{ + if ( tree != 0 ) + tree->refs += 1; +} + +void treeDownref( Program *prg, Tree **sp, Tree *tree ) +{ + if ( tree != 0 ) { + assert( tree->refs > 0 ); + tree->refs -= 1; + if ( tree->refs == 0 ) + treeFreeRec( prg, sp, tree ); + } +} + +/* Find the first child of a tree. */ +Kid *treeChild( Program *prg, const Tree *tree ) +{ + LangElInfo *lelInfo = prg->rtd->lelInfo; + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + /* Skip over attributes. */ + long objectLength = lelInfo[tree->id].objectLength; + long a; + for ( a = 0; a < objectLength; a++ ) + kid = kid->next; + + return kid; +} + +/* Detach at the first real child of a tree. */ +Kid *treeExtractChild( Program *prg, Tree *tree ) +{ + LangElInfo *lelInfo = prg->rtd->lelInfo; + Kid *kid = tree->child, *last = 0; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + /* Skip over attributes. */ + long a, objectLength = lelInfo[tree->id].objectLength; + for ( a = 0; a < objectLength; a++ ) { + last = kid; + kid = kid->next; + } + + if ( last == 0 ) + tree->child = 0; + else + last->next = 0; + + return kid; +} + + +/* Find the first child of a tree. */ +Kid *treeAttr( Program *prg, const Tree *tree ) +{ + Kid *kid = tree->child; + + if ( tree->flags & AF_LEFT_IGNORE ) + kid = kid->next; + if ( tree->flags & AF_RIGHT_IGNORE ) + kid = kid->next; + + return kid; +} + +Tree *treeLeftIgnore( Program *prg, Tree *tree ) +{ + if ( tree->flags & AF_LEFT_IGNORE ) + return tree->child->tree; + return 0; +} + +Tree *treeRightIgnore( Program *prg, Tree *tree ) +{ + if ( tree->flags & AF_RIGHT_IGNORE ) { + if ( tree->flags & AF_LEFT_IGNORE ) + return tree->child->next->tree; + else + return tree->child->tree; + } + return 0; +} + +Kid *treeLeftIgnoreKid( Program *prg, Tree *tree ) +{ + if ( tree->flags & AF_LEFT_IGNORE ) + return tree->child; + return 0; +} + +Kid *treeRightIgnoreKid( Program *prg, Tree *tree ) +{ + if ( tree->flags & AF_RIGHT_IGNORE ) { + if ( tree->flags & AF_LEFT_IGNORE ) + return tree->child->next; + else + return tree->child; + } + return 0; +} + +void refSetValue( Program *prg, Tree **sp, Ref *ref, Tree *v ) +{ + treeDownref( prg, sp, ref->kid->tree ); + ref->kid->tree = v; +} + +Tree *getRhsEl( Program *prg, Tree *lhs, long position ) +{ + Kid *pos = treeChild( prg, lhs ); + while ( position > 0 ) { + pos = pos->next; + position -= 1; + } + return pos->tree; +} + +Tree *colm_get_rhs_val( Program *prg, Tree *tree, int *a ) +{ + int i, len = a[0]; + for ( i = 0; i < len; i++ ) { + int prodNum = a[1 + i * 2]; + int childNum = a[1 + i * 2 + 1]; + if ( tree->prodNum == prodNum ) + return getRhsEl( prg, tree, childNum ); + } + return 0; +} + +void setField( Program *prg, Tree *tree, long field, Tree *value ) +{ + assert( tree->refs == 1 ); + if ( value != 0 ) + assert( value->refs >= 1 ); + setAttr( tree, field, value ); +} + +Tree *getField( Tree *tree, Word field ) +{ + return colm_get_attr( tree, field ); +} + +Kid *getFieldKid( Tree *tree, Word field ) +{ + return getAttrKid( tree, field ); +} + +Tree *getFieldSplit( Program *prg, Tree *tree, Word field ) +{ + Tree *val = colm_get_attr( tree, field ); + Tree *split = splitTree( prg, val ); + setAttr( tree, field, split ); + return split; +} + +Tree *getPtrVal( Pointer *ptr ) +{ + return ptr->value->tree; +} + +Tree *getPtrValSplit( Program *prg, Pointer *ptr ) +{ + Tree *val = ptr->value->tree; + Tree *split = splitTree( prg, val ); + ptr->value->tree = split; + return split; +} + +/* This must traverse in the same order that the bindId assignments are done + * in. */ +int matchPattern( Tree **bindings, Program *prg, long pat, Kid *kid, int checkNext ) +{ + PatConsNode *nodes = prg->rtd->patReplNodes; + + /* match node, recurse on children. */ + if ( pat != -1 && kid != 0 ) { + if ( nodes[pat].id == kid->tree->id ) { + /* If the pattern node has data, then this means we need to match + * the data against the token data. */ + if ( nodes[pat].data != 0 ) { + /* Check the length of token text. */ + if ( nodes[pat].length != stringLength( kid->tree->tokdata ) ) + return false; + + /* Check the token text data. */ + if ( nodes[pat].length > 0 && memcmp( nodes[pat].data, + stringData( kid->tree->tokdata ), nodes[pat].length ) != 0 ) + return false; + } + + /* No failure, all okay. */ + if ( nodes[pat].bindId > 0 ) { + bindings[nodes[pat].bindId] = kid->tree; + } + + /* If we didn't match a terminal duplicate of a nonterm then check + * down the children. */ + if ( !nodes[pat].stop ) { + /* Check for failure down child branch. */ + int childCheck = matchPattern( bindings, prg, + nodes[pat].child, treeChild( prg, kid->tree ), true ); + if ( ! childCheck ) + return false; + } + + /* If checking next, then look for failure there. */ + if ( checkNext ) { + int nextCheck = matchPattern( bindings, prg, + nodes[pat].next, kid->next, true ); + if ( ! nextCheck ) + return false; + } + + return true; + } + } + else if ( pat == -1 && kid == 0 ) { + /* Both null is a match. */ + return 1; + } + + return false; +} + + +long cmpTree( Program *prg, const Tree *tree1, const Tree *tree2 ) +{ + long cmpres = 0; + if ( tree1 == 0 ) { + if ( tree2 == 0 ) + return 0; + else + return -1; + } + else if ( tree2 == 0 ) + return 1; + else if ( tree1->id < tree2->id ) + return -1; + else if ( tree1->id > tree2->id ) + return 1; + else if ( tree1->id == LEL_ID_PTR ) { + if ( ((Pointer*)tree1)->value < ((Pointer*)tree2)->value ) + return -1; + else if ( ((Pointer*)tree1)->value > ((Pointer*)tree2)->value ) + return 1; + } + else if ( tree1->id == LEL_ID_INT ) { + if ( ((Int*)tree1)->value < ((Int*)tree2)->value ) + return -1; + else if ( ((Int*)tree1)->value > ((Int*)tree2)->value ) + return 1; + } + else if ( tree1->id == LEL_ID_STR ) { + cmpres = cmpString( ((Str*)tree1)->value, ((Str*)tree2)->value ); + if ( cmpres != 0 ) + return cmpres; + } + else { + if ( tree1->tokdata == 0 && tree2->tokdata != 0 ) + return -1; + else if ( tree1->tokdata != 0 && tree2->tokdata == 0 ) + return 1; + else if ( tree1->tokdata != 0 && tree2->tokdata != 0 ) { + cmpres = cmpString( tree1->tokdata, tree2->tokdata ); + if ( cmpres != 0 ) + return cmpres; + } + } + + Kid *kid1 = treeChild( prg, tree1 ); + Kid *kid2 = treeChild( prg, tree2 ); + + while ( true ) { + if ( kid1 == 0 && kid2 == 0 ) + return 0; + else if ( kid1 == 0 && kid2 != 0 ) + return -1; + else if ( kid1 != 0 && kid2 == 0 ) + return 1; + else { + cmpres = cmpTree( prg, kid1->tree, kid2->tree ); + if ( cmpres != 0 ) + return cmpres; + } + kid1 = kid1->next; + kid2 = kid2->next; + } +} + + +void splitRef( Program *prg, Tree ***psp, Ref *fromRef ) +{ + /* Go up the chain of kids, turing the pointers down. */ + Ref *last = 0, *ref = fromRef, *next = 0; + while ( ref->next != 0 ) { + next = ref->next; + ref->next = last; + last = ref; + ref = next; + } + ref->next = last; + + /* Now traverse the list, which goes down. */ + while ( ref != 0 ) { + if ( ref->kid->tree->refs > 1 ) { + Ref *nextDown = ref->next; + while ( nextDown != 0 && nextDown->kid == ref->kid ) + nextDown = nextDown->next; + + Kid *oldNextKidDown = nextDown != 0 ? nextDown->kid : 0; + Kid *newNextKidDown = 0; + + Tree *newTree = copyTree( prg, ref->kid->tree, + oldNextKidDown, &newNextKidDown ); + treeUpref( newTree ); + + /* Downref the original. Don't need to consider freeing because + * refs were > 1. */ + ref->kid->tree->refs -= 1; + + while ( ref != 0 && ref != nextDown ) { + next = ref->next; + ref->next = 0; + + ref->kid->tree = newTree; + ref = next; + } + + /* Correct kid pointers down from ref. */ + while ( nextDown != 0 && nextDown->kid == oldNextKidDown ) { + nextDown->kid = newNextKidDown; + nextDown = nextDown->next; + } + } + else { + /* Reset the list as we go down. */ + next = ref->next; + ref->next = 0; + ref = next; + } + } +} + +Tree *setListMem( List *list, Half field, Tree *value ) +{ + assert( list->refs == 1 ); + if ( value != 0 ) + assert( value->refs >= 1 ); + + Tree *existing = 0; + switch ( field ) { + case 0: + existing = list->head->value; + list->head->value = value; + break; + case 1: + existing = list->tail->value; + list->tail->value = value; + break; + default: + assert( false ); + break; + } + return existing; +} + +TreePair mapRemove( Program *prg, Map *map, Tree *key ) +{ + MapEl *mapEl = mapImplFind( prg, map, key ); + TreePair result = { 0, 0 }; + if ( mapEl != 0 ) { + mapDetach( prg, map, mapEl ); + result.key = mapEl->key; + result.val = mapEl->tree; + mapElFree( prg, mapEl ); + } + + return result; +} + +Tree *mapUnstore( Program *prg, Map *map, Tree *key, Tree *existing ) +{ + Tree *stored = 0; + if ( existing == 0 ) { + MapEl *mapEl = mapDetachByKey( prg, map, key ); + stored = mapEl->tree; + mapElFree( prg, mapEl ); + } + else { + MapEl *mapEl = mapImplFind( prg, map, key ); + stored = mapEl->tree; + mapEl->tree = existing; + } + return stored; +} + +Tree *mapFind( Program *prg, Map *map, Tree *key ) +{ + MapEl *mapEl = mapImplFind( prg, map, key ); + return mapEl == 0 ? 0 : mapEl->tree; +} + +long mapLength( Map *map ) +{ + return map->treeSize; +} + +void listAppend2( Program *prg, List *list, Tree *val ) +{ + assert( list->refs == 1 ); + if ( val != 0 ) + assert( val->refs >= 1 ); + ListEl *listEl = listElAllocate( prg ); + listEl->value = val; + listAppend( list, listEl ); +} + +Tree *listRemoveEnd( Program *prg, List *list ) +{ + Tree *tree = list->tail->value; + listElFree( prg, listDetachLast( list ) ); + return tree; +} + +Tree *getListMem( List *list, Word field ) +{ + Tree *result = 0; + switch ( field ) { + case 0: + result = list->head->value; + break; + case 1: + result = list->tail->value; + break; + default: + assert( false ); + break; + } + return result; +} + +Tree *getParserMem( Parser *parser, Word field ) +{ + Tree *result = 0; + switch ( field ) { + case 0: + result = parser->result; + break; + case 1: + result = parser->pdaRun->parseErrorText; + break; + default: + assert( false ); + break; + } + return result; +} + +Tree *getListMemSplit( Program *prg, List *list, Word field ) +{ + Tree *sv = 0; + switch ( field ) { + case 0: + sv = splitTree( prg, list->head->value ); + list->head->value = sv; + break; + case 1: + sv = splitTree( prg, list->tail->value ); + list->tail->value = sv; + break; + default: + assert( false ); + break; + } + return sv; +} + + +int mapInsert( Program *prg, Map *map, Tree *key, Tree *element ) +{ + MapEl *mapEl = mapInsertKey( prg, map, key, 0 ); + + if ( mapEl != 0 ) { + mapEl->tree = element; + return true; + } + + return false; +} + +void mapUnremove( Program *prg, Map *map, Tree *key, Tree *element ) +{ + MapEl *mapEl = mapInsertKey( prg, map, key, 0 ); + assert( mapEl != 0 ); + mapEl->tree = element; +} + +Tree *mapUninsert( Program *prg, Map *map, Tree *key ) +{ + MapEl *el = mapDetachByKey( prg, map, key ); + Tree *val = el->tree; + mapElFree( prg, el ); + return val; +} + +Tree *mapStore( Program *prg, Map *map, Tree *key, Tree *element ) +{ + Tree *oldTree = 0; + MapEl *elInTree = 0; + MapEl *mapEl = mapInsertKey( prg, map, key, &elInTree ); + + if ( mapEl != 0 ) + mapEl->tree = element; + else { + /* Element with key exists. Overwriting the value. */ + oldTree = elInTree->tree; + elInTree->tree = element; + } + + return oldTree; +} + +static Tree *treeSearchKid( Program *prg, Kid *kid, long id ) +{ + /* This node the one? */ + if ( kid->tree->id == id ) + return kid->tree; + + Tree *res = 0; + + /* Search children. */ + Kid *child = treeChild( prg, kid->tree ); + if ( child != 0 ) + res = treeSearchKid( prg, child, id ); + + /* Search siblings. */ + if ( res == 0 && kid->next != 0 ) + res = treeSearchKid( prg, kid->next, id ); + + return res; +} + +Tree *treeSearch( Program *prg, Tree *tree, long id ) +{ + Tree *res = 0; + if ( tree->id == id ) + res = tree; + else { + Kid *child = treeChild( prg, tree ); + if ( child != 0 ) + res = treeSearchKid( prg, child, id ); + } + return res; +} + +static Location *locSearchKid( Program *prg, Kid *kid ) +{ + /* This node the one? */ + if ( kid->tree->tokdata != 0 && kid->tree->tokdata->location != 0 ) + return kid->tree->tokdata->location; + + Location *res = 0; + + /* Search children. */ + Kid *child = treeChild( prg, kid->tree ); + if ( child != 0 ) + res = locSearchKid( prg, child ); + + /* Search siblings. */ + if ( res == 0 && kid->next != 0 ) + res = locSearchKid( prg, kid->next ); + + return res; +} + +Location *locSearch( Program *prg, Tree *tree ) +{ + Location *res = 0; + if ( tree->tokdata != 0 && tree->tokdata->location != 0 ) + return tree->tokdata->location; + + Kid *child = treeChild( prg, tree ); + if ( child != 0 ) + res = locSearchKid( prg, child ); + + return res; +} + +struct colm_location *colm_find_location( Program *prg, Tree *tree ) +{ + return locSearch( prg, tree ); +} + +void xmlEscapeData( struct colm_print_args *printArgs, const char *data, long len ) +{ + int i; + for ( i = 0; i < len; i++ ) { + if ( data[i] == '<' ) + printArgs->out( printArgs, "<", 4 ); + else if ( data[i] == '>' ) + printArgs->out( printArgs, ">", 4 ); + else if ( data[i] == '&' ) + printArgs->out( printArgs, "&", 5 ); + else if ( (32 <= data[i] && data[i] <= 126) || + data[i] == '\t' || data[i] == '\n' || data[i] == '\r' ) + { + printArgs->out( printArgs, &data[i], 1 ); + } + else { + char out[64]; + sprintf( out, "&#%u;", ((unsigned)data[i]) ); + printArgs->out( printArgs, out, strlen(out) ); + } + } +} + +void initStrCollect( StrCollect *collect ) +{ + collect->data = (char*) malloc( BUFFER_INITIAL_SIZE ); + collect->allocated = BUFFER_INITIAL_SIZE; + collect->length = 0; +} + +void strCollectDestroy( StrCollect *collect ) +{ + free( collect->data ); +} + +void strCollectAppend( StrCollect *collect, const char *data, long len ) +{ + long newLen = collect->length + len; + if ( newLen > collect->allocated ) { + collect->allocated = newLen * 2; + collect->data = (char*) realloc( collect->data, collect->allocated ); + } + memcpy( collect->data + collect->length, data, len ); + collect->length += len; +} + +void strCollectClear( StrCollect *collect ) +{ + collect->length = 0; +} + +#define INT_SZ 32 + +void printStr( struct colm_print_args *printArgs, Head *str ) +{ + printArgs->out( printArgs, (char*)(str->data), str->length ); +} + +void appendCollect( struct colm_print_args *args, const char *data, int length ) +{ + strCollectAppend( (StrCollect*) args->arg, data, length ); +} + +void appendFile( struct colm_print_args *args, const char *data, int length ) +{ + fwrite( data, 1, length, (FILE*)args->arg ); +} + +void appendFd( struct colm_print_args *args, const char *data, int length ) +{ + int fd = (long)args->arg; + int res = write( fd, data, length ); + if ( res < 0 ) + message( "write error on fd: %d: %s\n", fd, strerror(errno) ); +} + +Tree *treeTrim( struct colm_program *prg, Tree **sp, Tree *tree ) +{ + debug( prg, REALM_PARSE, "attaching left ignore\n" ); + + /* Make the ignore list for the left-ignore. */ + Tree *leftIgnore = treeAllocate( prg ); + leftIgnore->id = LEL_ID_IGNORE; + leftIgnore->flags |= AF_SUPPRESS_RIGHT; + + tree = pushLeftIgnore( prg, tree, leftIgnore ); + + debug( prg, REALM_PARSE, "attaching ignore right\n" ); + + /* Copy the ignore list first if we need to attach it as a right + * ignore. */ + Tree *rightIgnore = 0; + rightIgnore = treeAllocate( prg ); + rightIgnore->id = LEL_ID_IGNORE; + rightIgnore->flags |= AF_SUPPRESS_LEFT; + + tree = pushRightIgnore( prg, tree, rightIgnore ); + + return tree; +} + +enum ReturnType +{ + Done = 1, + CollectIgnoreLeft, + CollectIgnoreRight, + RecIgnoreList, + ChildPrint +}; + +enum VisitType +{ + IgnoreWrapper, + IgnoreData, + Term, + NonTerm, +}; + +#define TF_TERM_SEEN 0x1 + +void printKid( Program *prg, Tree **sp, struct colm_print_args *printArgs, Kid *kid ) +{ + enum ReturnType rt; + Kid *parent = 0; + Kid *leadingIgnore = 0; + enum VisitType visitType; + int flags = 0; + + /* Iterate the kids passed in. We are expecting a next, which will allow us + * to print the trailing ignore list. */ + while ( kid != 0 ) { + vm_push( (SW) Done ); + goto rec_call; + rec_return_top: + kid = kid->next; + } + + return; + +rec_call: + if ( kid->tree == 0 ) + goto skip_null; + + /* If not currently skipping ignore data, then print it. Ignore data can + * be associated with terminals and nonterminals. */ + if ( kid->tree->flags & AF_LEFT_IGNORE ) { + vm_push( (SW)parent ); + vm_push( (SW)kid ); + parent = kid; + kid = treeLeftIgnoreKid( prg, kid->tree ); + vm_push( (SW) CollectIgnoreLeft ); + goto rec_call; + rec_return_ign_left: + kid = (Kid*)vm_pop(); + parent = (Kid*)vm_pop(); + } + + if ( kid->tree->id == LEL_ID_IGNORE ) + visitType = IgnoreWrapper; + else if ( parent != 0 && parent->tree->id == LEL_ID_IGNORE ) + visitType = IgnoreData; + else if ( kid->tree->id < prg->rtd->firstNonTermId ) + visitType = Term; + else + visitType = NonTerm; + + debug( prg, REALM_PRINT, "visit type: %d\n", visitType ); + + if ( visitType == IgnoreData ) { + debug( prg, REALM_PRINT, "putting %p on ignore list\n", kid->tree ); + Kid *newIgnore = kidAllocate( prg ); + newIgnore->next = leadingIgnore; + leadingIgnore = newIgnore; + leadingIgnore->tree = kid->tree; + goto skip_node; + } + + if ( visitType == IgnoreWrapper ) { + Kid *newIgnore = kidAllocate( prg ); + newIgnore->next = leadingIgnore; + leadingIgnore = newIgnore; + leadingIgnore->tree = kid->tree; + /* Don't skip. */ + } + + /* print leading ignore? Triggered by terminals. */ + if ( visitType == Term ) { + /* Reverse the leading ignore list. */ + if ( leadingIgnore != 0 ) { + Kid *ignore = 0, *last = 0; + + /* Reverse the list and take the opportunity to implement the + * suppress left. */ + while ( true ) { + Kid *next = leadingIgnore->next; + leadingIgnore->next = last; + + if ( leadingIgnore->tree->flags & AF_SUPPRESS_LEFT ) { + /* We are moving left. Chop off the tail. */ + debug( prg, REALM_PRINT, "suppressing left\n" ); + freeKidList( prg, next ); + break; + } + + if ( next == 0 ) + break; + + last = leadingIgnore; + leadingIgnore = next; + } + + /* Print the leading ignore list. Also implement the suppress right + * in the process. */ + if ( printArgs->comm && (!printArgs->trim || (flags & TF_TERM_SEEN && kid->tree->id > 0)) ) { + ignore = leadingIgnore; + while ( ignore != 0 ) { + if ( ignore->tree->flags & AF_SUPPRESS_RIGHT ) + break; + + if ( ignore->tree->id != LEL_ID_IGNORE ) { + vm_push( (SW)visitType ); + vm_push( (SW)leadingIgnore ); + vm_push( (SW)ignore ); + vm_push( (SW)parent ); + vm_push( (SW)kid ); + + leadingIgnore = 0; + kid = ignore; + parent = 0; + + debug( prg, REALM_PRINT, "rec call on %p\n", kid->tree ); + vm_push( (SW) RecIgnoreList ); + goto rec_call; + rec_return_il: + + kid = (Kid*)vm_pop(); + parent = (Kid*)vm_pop(); + ignore = (Kid*)vm_pop(); + leadingIgnore = (Kid*)vm_pop(); + visitType = (enum VisitType)vm_pop(); + } + + ignore = ignore->next; + } + } + + /* Free the leading ignore list. */ + freeKidList( prg, leadingIgnore ); + leadingIgnore = 0; + } + } + + if ( visitType == Term || visitType == NonTerm ) { + /* Open the tree. */ + printArgs->open_tree( prg, sp, printArgs, parent, kid ); + } + + if ( visitType == Term ) + flags |= TF_TERM_SEEN; + + if ( visitType == Term || visitType == IgnoreData ) { + /* Print contents. */ + if ( kid->tree->id < prg->rtd->firstNonTermId ) { + debug( prg, REALM_PRINT, "printing terminal %p\n", kid->tree ); + if ( kid->tree->id != 0 ) + printArgs->print_term( prg, sp, printArgs, kid ); + } + } + + /* Print children. */ + Kid *child = printArgs->attr ? + treeAttr( prg, kid->tree ) : + treeChild( prg, kid->tree ); + + if ( child != 0 ) { + vm_push( (SW)visitType ); + vm_push( (SW)parent ); + vm_push( (SW)kid ); + parent = kid; + kid = child; + while ( kid != 0 ) { + vm_push( (SW) ChildPrint ); + goto rec_call; + rec_return: + kid = kid->next; + } + kid = (Kid*)vm_pop(); + parent = (Kid*)vm_pop(); + visitType = (enum VisitType)vm_pop(); + } + + if ( visitType == Term || visitType == NonTerm ) { + /* close the tree. */ + printArgs->close_tree( prg, sp, printArgs, parent, kid ); + } + +skip_node: + + /* If not currently skipping ignore data, then print it. Ignore data can + * be associated with terminals and nonterminals. */ + if ( kid->tree->flags & AF_RIGHT_IGNORE ) { + debug( prg, REALM_PRINT, "right ignore\n" ); + vm_push( (SW)parent ); + vm_push( (SW)kid ); + parent = kid; + kid = treeRightIgnoreKid( prg, kid->tree ); + vm_push( (SW) CollectIgnoreRight ); + goto rec_call; + rec_return_ign_right: + kid = (Kid*)vm_pop(); + parent = (Kid*)vm_pop(); + } + +/* For skiping over content on null. */ +skip_null: + + rt = (enum ReturnType)vm_pop(); + switch ( rt ) { + case Done: + debug( prg, REALM_PRINT, "return: done\n" ); + goto rec_return_top; + break; + case CollectIgnoreLeft: + debug( prg, REALM_PRINT, "return: ignore left\n" ); + goto rec_return_ign_left; + case CollectIgnoreRight: + debug( prg, REALM_PRINT, "return: ignore right\n" ); + goto rec_return_ign_right; + case RecIgnoreList: + debug( prg, REALM_PRINT, "return: ignore list\n" ); + goto rec_return_il; + case ChildPrint: + debug( prg, REALM_PRINT, "return: child print\n" ); + goto rec_return; + } +} + +void colm_print_tree_args( Program *prg, Tree **sp, struct colm_print_args *printArgs, Tree *tree ) +{ + if ( tree == 0 ) + printArgs->out( printArgs, "NIL", 3 ); + else { + /* This term tree allows us to print trailing ignores. */ + Tree termTree; + memset( &termTree, 0, sizeof(termTree) ); + + Kid kid, term; + term.tree = &termTree; + term.next = 0; + term.flags = 0; + + kid.tree = tree; + kid.next = &term; + kid.flags = 0; + + printKid( prg, sp, printArgs, &kid ); + } +} + +void colm_print_term_tree( Program *prg, Tree **sp, struct colm_print_args *printArgs, Kid *kid ) +{ + debug( prg, REALM_PRINT, "printing term %p\n", kid->tree ); + + if ( kid->tree->id == LEL_ID_INT ) { + char buf[INT_SZ]; + sprintf( buf, "%ld", ((Int*)kid->tree)->value ); + printArgs->out( printArgs, buf, strlen(buf) ); + } + else if ( kid->tree->id == LEL_ID_BOOL ) { + if ( ((Int*)kid->tree)->value ) + printArgs->out( printArgs, "true", 4 ); + else + printArgs->out( printArgs, "false", 5 ); + } + else if ( kid->tree->id == LEL_ID_PTR ) { + char buf[INT_SZ]; + printArgs->out( printArgs, "#", 1 ); + sprintf( buf, "%p", (void*) ((Pointer*)kid->tree)->value ); + printArgs->out( printArgs, buf, strlen(buf) ); + } + else if ( kid->tree->id == LEL_ID_STR ) { + printStr( printArgs, ((Str*)kid->tree)->value ); + } + else if ( kid->tree->id == LEL_ID_STREAM ) { + char buf[INT_SZ]; + printArgs->out( printArgs, "#", 1 ); + sprintf( buf, "%p", (void*) ((Stream*)kid->tree)->in->file ); + printArgs->out( printArgs, buf, strlen(buf) ); + } + else if ( kid->tree->tokdata != 0 && + stringLength( kid->tree->tokdata ) > 0 ) + { + printArgs->out( printArgs, stringData( kid->tree->tokdata ), + stringLength( kid->tree->tokdata ) ); + } +} + + +void colm_print_null( Program *prg, Tree **sp, struct colm_print_args *args, Kid *parent, Kid *kid ) +{ +} + +void openTreeXml( Program *prg, Tree **sp, struct colm_print_args *args, Kid *parent, Kid *kid ) +{ + /* Skip the terminal that is for forcing trailing ignores out. */ + if ( kid->tree->id == 0 ) + return; + + LangElInfo *lelInfo = prg->rtd->lelInfo; + + /* List flattening: skip the repeats and lists that are a continuation of + * the list. */ + if ( parent != 0 && parent->tree->id == kid->tree->id && kid->next == 0 && + ( lelInfo[parent->tree->id].repeat || lelInfo[parent->tree->id].list ) ) + { + return; + } + + const char *name = lelInfo[kid->tree->id].xmlTag; + args->out( args, "<", 1 ); + args->out( args, name, strlen( name ) ); + args->out( args, ">", 1 ); +} + +void printTermXml( Program *prg, Tree **sp, struct colm_print_args *printArgs, Kid *kid ) +{ + //Kid *child; + + /*child = */ treeChild( prg, kid->tree ); + if ( kid->tree->id == LEL_ID_PTR ) { + char ptr[32]; + sprintf( ptr, "%p\n", (void*)((Pointer*)kid->tree)->value ); + printArgs->out( printArgs, ptr, strlen(ptr) ); + } + else if ( kid->tree->id == LEL_ID_BOOL ) { + if ( ((Int*)kid->tree)->value ) + printArgs->out( printArgs, "true", 4 ); + else + printArgs->out( printArgs, "false", 5 ); + } + else if ( kid->tree->id == LEL_ID_INT ) { + char ptr[32]; + sprintf( ptr, "%ld", ((Int*)kid->tree)->value ); + printArgs->out( printArgs, ptr, strlen(ptr) ); + } + else if ( kid->tree->id == LEL_ID_STR ) { + Head *head = (Head*) ((Str*)kid->tree)->value; + + xmlEscapeData( printArgs, (char*)(head->data), head->length ); + } + else if ( 0 < kid->tree->id && kid->tree->id < prg->rtd->firstNonTermId && + kid->tree->id != LEL_ID_IGNORE && + kid->tree->tokdata != 0 && + stringLength( kid->tree->tokdata ) > 0 ) + { + xmlEscapeData( printArgs, stringData( kid->tree->tokdata ), + stringLength( kid->tree->tokdata ) ); + } +} + + +void closeTreeXml( Program *prg, Tree **sp, struct colm_print_args *args, Kid *parent, Kid *kid ) +{ + /* Skip the terminal that is for forcing trailing ignores out. */ + if ( kid->tree->id == 0 ) + return; + + LangElInfo *lelInfo = prg->rtd->lelInfo; + + /* List flattening: skip the repeats and lists that are a continuation of + * the list. */ + if ( parent != 0 && parent->tree->id == kid->tree->id && kid->next == 0 && + ( lelInfo[parent->tree->id].repeat || lelInfo[parent->tree->id].list ) ) + { + return; + } + + const char *name = lelInfo[kid->tree->id].xmlTag; + args->out( args, "</", 2 ); + args->out( args, name, strlen( name ) ); + args->out( args, ">", 1 ); +} + +void printTreeCollect( Program *prg, Tree **sp, StrCollect *collect, Tree *tree, int trim ) +{ + struct colm_print_args printArgs = { collect, true, false, trim, &appendCollect, + &colm_print_null, &colm_print_term_tree, &colm_print_null }; + colm_print_tree_args( prg, sp, &printArgs, tree ); +} + +void printTreeFile( Program *prg, Tree **sp, FILE *out, Tree *tree, int trim ) +{ + struct colm_print_args printArgs = { out, true, false, trim, &appendFile, + &colm_print_null, &colm_print_term_tree, &colm_print_null }; + colm_print_tree_args( prg, sp, &printArgs, tree ); +} + +void printTreeFd( Program *prg, Tree **sp, int fd, Tree *tree, int trim ) +{ + struct colm_print_args printArgs = { (void*)((long)fd), true, false, trim, &appendFd, + &colm_print_null, &colm_print_term_tree, &colm_print_null }; + colm_print_tree_args( prg, sp, &printArgs, tree ); +} + +void printXmlStdout( Program *prg, Tree **sp, Tree *tree, int commAttr, int trim ) +{ + struct colm_print_args printArgs = { stdout, commAttr, commAttr, trim, &appendFile, + &openTreeXml, &printTermXml, &closeTreeXml }; + colm_print_tree_args( prg, sp, &printArgs, tree ); +} + + |