diff options
Diffstat (limited to 'colm/compile.cpp')
-rw-r--r-- | colm/compile.cpp | 2770 |
1 files changed, 2770 insertions, 0 deletions
diff --git a/colm/compile.cpp b/colm/compile.cpp new file mode 100644 index 00000000..19f42293 --- /dev/null +++ b/colm/compile.cpp @@ -0,0 +1,2770 @@ +/* + * Copyright 2007 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 "bytecode.h" +#include "parsedata.h" +#include <iostream> +#include <assert.h> + +using std::cout; +using std::cerr; +using std::endl; + +void ParseData::initUniqueTypes( ) +{ + uniqueTypeNil = new UniqueType( TYPE_NIL ); + uniqueTypePtr = new UniqueType( TYPE_TREE, ptrKlangEl ); + uniqueTypeBool = new UniqueType( TYPE_TREE, boolKlangEl ); + uniqueTypeInt = new UniqueType( TYPE_TREE, intKlangEl ); + uniqueTypeStr = new UniqueType( TYPE_TREE, strKlangEl ); + uniqueTypeStream = new UniqueType( TYPE_TREE, streamKlangEl ); + uniqueTypeAny = new UniqueType( TYPE_TREE, anyKlangEl ); + + uniqeTypeMap.insert( uniqueTypeNil ); + uniqeTypeMap.insert( uniqueTypePtr ); + uniqeTypeMap.insert( uniqueTypeBool ); + uniqeTypeMap.insert( uniqueTypeInt ); + uniqeTypeMap.insert( uniqueTypeStr ); + uniqeTypeMap.insert( uniqueTypeStream ); + uniqeTypeMap.insert( uniqueTypeAny ); +} + +IterDef::IterDef( Type type ) : + type(type), + func(0), + useFuncId(false), + useSearchUT(false) +{ + if ( type == Tree ) { + inCreateWV = IN_TRITER_FROM_REF; + inCreateWC = IN_TRITER_FROM_REF; + inDestroy = IN_TRITER_DESTROY; + inAdvance = IN_TRITER_ADVANCE; + + inGetCurR = IN_TRITER_GET_CUR_R; + inGetCurWC = IN_TRITER_GET_CUR_WC; + inSetCurWC = IN_TRITER_SET_CUR_WC; + inRefFromCur = IN_TRITER_REF_FROM_CUR; + useSearchUT = true; + } + else if ( type == Child ) { + inCreateWV = IN_TRITER_FROM_REF; + inCreateWC = IN_TRITER_FROM_REF; + inDestroy = IN_TRITER_DESTROY; + inAdvance = IN_TRITER_NEXT_CHILD; + + inGetCurR = IN_TRITER_GET_CUR_R; + inGetCurWC = IN_TRITER_GET_CUR_WC; + inSetCurWC = IN_TRITER_SET_CUR_WC; + inRefFromCur = IN_TRITER_REF_FROM_CUR; + useSearchUT = true; + } + else if ( type == RevChild ) { + inCreateWV = IN_TRITER_FROM_REF; + inCreateWC = IN_TRITER_FROM_REF; + inDestroy = IN_TRITER_DESTROY; + inAdvance = IN_TRITER_PREV_CHILD; + + inGetCurR = IN_TRITER_GET_CUR_R; + inGetCurWC = IN_TRITER_GET_CUR_WC; + inSetCurWC = IN_TRITER_SET_CUR_WC; + inRefFromCur = IN_TRITER_REF_FROM_CUR; + useSearchUT = true; + } + else + assert(false); +} + +IterDef::IterDef( Type type, Function *func ) : + type(type), + func(func), + useFuncId(true), + useSearchUT(true), + inCreateWV(IN_UITER_CREATE_WV), + inCreateWC(IN_UITER_CREATE_WC), + inDestroy(IN_UITER_DESTROY), + inAdvance(IN_UITER_ADVANCE), + inGetCurR(IN_UITER_GET_CUR_R), + inGetCurWC(IN_UITER_GET_CUR_WC), + inSetCurWC(IN_UITER_SET_CUR_WC), + inRefFromCur(IN_UITER_REF_FROM_CUR) +{} + +ObjMethod *initFunction( UniqueType *retType, ObjectDef *obj, + const String &name, int methIdWV, int methIdWC, bool isConst ) +{ + ObjMethod *objMethod = new ObjMethod( retType, name, + methIdWV, methIdWC, 0, 0, 0, isConst ); + obj->objMethodMap->insert( name, objMethod ); + return objMethod; +} + +ObjMethod *initFunction( UniqueType *retType, ObjectDef *obj, + const String &name, int methIdWV, int methIdWC, UniqueType *arg1, bool isConst ) +{ + UniqueType *args[] = { arg1 }; + ObjMethod *objMethod = new ObjMethod( retType, name, + methIdWV, methIdWC, 1, args, 0, isConst ); + obj->objMethodMap->insert( name, objMethod ); + return objMethod; +} + +ObjMethod *initFunction( UniqueType *retType, ObjectDef *obj, + const String &name, int methIdWV, int methIdWC, + UniqueType *arg1, UniqueType *arg2, bool isConst ) +{ + UniqueType *args[] = { arg1, arg2 }; + ObjMethod *objMethod = new ObjMethod( retType, name, + methIdWV, methIdWC, 2, args, 0, isConst ); + obj->objMethodMap->insert( name, objMethod ); + return objMethod; +} + +IterDef *ParseData::findIterDef( IterDef::Type type, Function *func ) +{ + IterDefSetEl *el = iterDefSet.find( IterDef( type, func ) ); + if ( el == 0 ) + el = iterDefSet.insert( IterDef( type, func ) ); + return &el->key; +} + +IterDef *ParseData::findIterDef( IterDef::Type type ) +{ + IterDefSetEl *el = iterDefSet.find( IterDef( type ) ); + if ( el == 0 ) + el = iterDefSet.insert( IterDef( type ) ); + return &el->key; +} + +UniqueType *ParseData::findUniqueType( int typeId ) +{ + UniqueType searchKey( typeId ); + UniqueType *uniqueType = uniqeTypeMap.find( &searchKey ); + if ( uniqueType == 0 ) { + uniqueType = new UniqueType( typeId ); + uniqeTypeMap.insert( uniqueType ); + } + return uniqueType; +} + +UniqueType *ParseData::findUniqueType( int typeId, KlangEl *langEl ) +{ + UniqueType searchKey( typeId, langEl ); + UniqueType *uniqueType = uniqeTypeMap.find( &searchKey ); + if ( uniqueType == 0 ) { + uniqueType = new UniqueType( typeId, langEl ); + uniqeTypeMap.insert( uniqueType ); + } + return uniqueType; +} + +UniqueType *ParseData::findUniqueType( int typeId, IterDef *iterDef ) +{ + UniqueType searchKey( typeId, iterDef ); + UniqueType *uniqueType = uniqeTypeMap.find( &searchKey ); + if ( uniqueType == 0 ) { + uniqueType = new UniqueType( typeId, iterDef ); + uniqeTypeMap.insert( uniqueType ); + } + return uniqueType; +} + +UniqueType *TypeRef::lookupTypePart( ParseData *pd, + NamespaceQual *qual, const String &name ) +{ + /* Lookup up the qualifiction and then the name. */ + Namespace *nspace = qual->getQual( pd ); + + if ( nspace == 0 ) + error(loc) << "do not have region for resolving reference" << endp; + + /* Search for the token in the region by name. */ + SymbolMapEl *inDict = nspace->symbolMap.find( name ); + if ( inDict != 0 ) { + long typeId = ( isPtr ? TYPE_PTR : ( isRef ? TYPE_REF : TYPE_TREE ) ); + return pd->findUniqueType( typeId, inDict->value ); + } + + error(loc) << "unknown type in typeof expression" << endp; + return 0; +} + +UniqueType *TypeRef::lookupType( ParseData *pd ) +{ + if ( uniqueType != 0 ) + return uniqueType; + + if ( iterDef != 0 ) + uniqueType = pd->findUniqueType( TYPE_ITER, iterDef ); + else if ( factor != 0 ) + uniqueType = pd->findUniqueType( TYPE_TREE, factor->langEl ); + else { + String name = typeName; + if ( repeatType == RepeatOpt ) + name.setAs( 32, "_opt_%s", name.data ); + else if ( repeatType == RepeatRepeat ) + name.setAs( 32, "_repeat_%s", name.data ); + else if ( repeatType == RepeatList ) + name.setAs( 32, "_list_%s", name.data ); + + /* Not an iterator. May be a reference. */ + uniqueType = lookupTypePart( pd, nspaceQual, name ); + } + + return uniqueType; +} + +ObjField *ObjectDef::findField( String name ) +{ + ObjFieldMapEl *objDefMapEl = objFieldMap->find( name ); + if ( objDefMapEl != 0 ) + return objDefMapEl->value; + return 0; +} + +ObjMethod *ObjectDef::findMethod( String name ) +{ + ObjMethodMapEl *objMethodMapEl = objMethodMap->find( name ); + if ( objMethodMapEl != 0 ) + return objMethodMapEl->value; + return 0; +} + +long sizeOfField( UniqueType *fieldUT ) +{ + long size = 0; + if ( fieldUT->typeId == TYPE_ITER ) { + /* Select on the iterator type. */ + if ( fieldUT->iterDef->type == IterDef::Tree || + fieldUT->iterDef->type == IterDef::Child || + fieldUT->iterDef->type == IterDef::RevChild ) + size = sizeof(TreeIter) / sizeof(Word); + else if ( fieldUT->iterDef->type == IterDef::User ) { + /* User iterators are just a pointer to the UserIter struct. The + * struct needs to go right beneath the call to the user iterator + * so it can be found by a yield. It is therefore allocated on the + * stack right before the call. */ + size = 1; + } + else { + assert(false); + } + } + else if ( fieldUT->typeId == TYPE_REF ) + size = 2; + else + size = 1; + + return size; +} + +void ObjectDef::referenceField( ParseData *pd, ObjField *field ) +{ + field->beenReferenced = true; + initField( pd, field ); +} + +void ObjectDef::initField( ParseData *pd, ObjField *field ) +{ + if ( !field->beenInitialized ) { + field->beenInitialized = true; + UniqueType *fieldUT = field->typeRef->lookupType( pd ); + + if ( type == FrameType ) { + nextOffset += sizeOfField( fieldUT ); + field->offset = -nextOffset; + + pd->initLocalInstructions( field ); + } + else { + field->offset = nextOffset; + nextOffset += sizeOfField( fieldUT ); + + /* Initialize the instructions. */ + pd->initFieldInstructions( field ); + } + } +} + +UniqueType *LangVarRef::loadFieldInstr( ParseData *pd, CodeVect &code, + ObjectDef *inObject, ObjField *el, bool forWriting, bool revert ) const +{ + /* Ensure that the field is referenced. */ + inObject->referenceField( pd, el ); + + UniqueType *elUT = el->typeRef->lookupType( pd ); + + /* If it's a reference then we load it read always. */ + if ( forWriting ) { + /* The instruction, depends on whether or not we are reverting. */ + if ( elUT->typeId == TYPE_ITER ) + code.append( elUT->iterDef->inGetCurWC ); + else if ( pd->revertOn && revert ) + code.append( el->inGetWV ); + else + code.append( el->inGetWC ); + } + else { + /* Loading something for writing */ + if ( elUT->typeId == TYPE_ITER ) + code.append( elUT->iterDef->inGetCurR ); + else + code.append( el->inGetR ); + } + + if ( el->useOffset ) { + /* Gets of locals and fields require offsets. Fake vars like token + * data and lhs don't require it. */ + code.appendHalf( el->offset ); + } + + /* If we are dealing with an iterator then dereference it. */ + if ( elUT->typeId == TYPE_ITER ) + elUT = el->typeRef->searchTypeRef->lookupType( pd ); + + return elUT; +} + +ObjectDef *objDefFromUT( ParseData *pd, UniqueType *ut ) +{ + ObjectDef *objDef = 0; + if ( ut->typeId == TYPE_TREE || ut->typeId == TYPE_REF ) + objDef = ut->langEl->objectDef; + else { + /* This should have generated a compiler error. */ + assert(false); + } + return objDef; +} + +/* The qualification must start at a local frame. There cannot be any pointer. */ +long LangVarRef::loadQualificationRefs( ParseData *pd, CodeVect &code ) const +{ + long count = 0; + ObjectDef *rootObj = pd->curLocalFrame; + + /* Start the search from the root object. */ + ObjectDef *searchObjDef = rootObj; + + for ( QualItemVect::Iter qi = *qual; qi.lte(); qi++ ) { + /* Lookup the field in the current qualification. */ + ObjFieldMapEl *objDefMapEl = searchObjDef->objFieldMap->find( qi->data ); + if ( objDefMapEl == 0 ) + error(qi->loc) << "cannot resolve qualification " << qi->data << endp; + ObjField *el = objDefMapEl->value; + + if ( qi.pos() > 0 ) { + code.append( IN_REF_FROM_QUAL_REF ); + code.appendHalf( 0 ); + code.appendHalf( el->offset ); + } + else if ( el->typeRef->iterDef != 0 ) { + code.append( el->typeRef->iterDef->inRefFromCur ); + code.appendHalf( el->offset ); + } + else if ( el->typeRef->isRef ) { + code.append( IN_REF_FROM_REF ); + code.appendHalf( el->offset ); + } + else { + code.append( IN_REF_FROM_LOCAL ); + code.appendHalf( el->offset ); + } + + UniqueType *elUT = el->typeRef->lookupType( pd ); + if ( elUT->typeId == TYPE_ITER ) + elUT = el->typeRef->searchTypeRef->lookupType( pd ); + + assert( qi->type == QualItem::Dot ); + + searchObjDef = objDefFromUT( pd, elUT ); + count += 1; + } + return count; +} + +void LangVarRef::loadQualification( ParseData *pd, CodeVect &code, + ObjectDef *rootObj, int lastPtrInQual, bool forWriting, bool revert ) const +{ + /* Start the search from the root object. */ + ObjectDef *searchObjDef = rootObj; + + for ( QualItemVect::Iter qi = *qual; qi.lte(); qi++ ) { + /* Lookup the field int the current qualification. */ + ObjFieldMapEl *objDefMapEl = searchObjDef->objFieldMap->find( qi->data ); + if ( objDefMapEl == 0 ) + error(qi->loc) << "cannot resolve qualification " << qi->data << endp; + ObjField *el = objDefMapEl->value; + + if ( forWriting && el->refActive ) + error(qi->loc) << "reference active, cannot write to object" << endp; + + bool lfForWriting = forWriting; + bool lfRevert = revert; + + /* If there is a pointer in the qualification, we need to compute + * forWriting and revert. */ + if ( lastPtrInQual >= 0 ) { + if ( qi.pos() <= lastPtrInQual ) { + /* If we are before or at the pointer we are strictly read + * only, regardless of the origin. */ + lfForWriting = false; + lfRevert = false; + } + else { + /* If we are past the pointer then we are always reverting + * because the object is global. Forwriting is as passed in. + * */ + lfRevert = true; + } + } + + UniqueType *qualUT = loadFieldInstr( pd, code, searchObjDef, + el, lfForWriting, lfRevert ); + + if ( qi->type == QualItem::Dot ) { + /* Cannot a reference. Iterator yes (access of the iterator not + * hte current) */ + if ( qualUT->typeId == TYPE_PTR ) + error(loc) << "dot cannot be used to access a pointer" << endp; + } + else if ( qi->type == QualItem::Arrow ) { + if ( qualUT->typeId == TYPE_PTR ) { + /* Always dereference references when used for qualification. If + * this is the last one then we must start with the reverse + * execution business. */ + if ( pd->revertOn && qi.pos() == lastPtrInQual && forWriting ) { + /* This is like a global load. */ + code.append( IN_PTR_DEREF_WV ); + } + else { + /* If reading or not yet the last in ref then we only need a + * reading deref. */ + code.append( IN_PTR_DEREF_R ); + } + + qualUT = pd->findUniqueType( TYPE_TREE, qualUT->langEl ); + } + else { + error(loc) << "arrow operator cannot be used to access this type" << endp; + } + } + + searchObjDef = objDefFromUT( pd, qualUT ); + } +} + +void LangVarRef::loadGlobalObj( ParseData *pd, CodeVect &code, + int lastPtrInQual, bool forWriting ) const +{ + /* Start the search in the global object. */ + ObjectDef *rootObj = pd->globalObjectDef; + + if ( forWriting && lastPtrInQual < 0 ) { + /* If we are writing an no reference was found in the qualification + * then load the gloabl with a revert. */ + if ( pd->revertOn ) + code.append( IN_LOAD_GLOBAL_WV ); + else + code.append( IN_LOAD_GLOBAL_WC ); + } + else { + /* Either we are reading or we are loading a pointer that will be + * dereferenced. */ + code.append( IN_LOAD_GLOBAL_R ); + } + + loadQualification( pd, code, rootObj, lastPtrInQual, forWriting, true ); +} + +void LangVarRef::loadLocalObj( ParseData *pd, CodeVect &code, + int lastPtrInQual, bool forWriting ) const +{ + /* Start the search in the local frame. */ + ObjectDef *rootObj = pd->curLocalFrame; + loadQualification( pd, code, rootObj, lastPtrInQual, forWriting, false ); +} + +bool LangVarRef::isLocalRef( ParseData *pd ) const +{ + if ( qual->length() > 0 ) { + if ( pd->curLocalFrame->objFieldMap->find( qual->data[0].data ) != 0 ) + return true; + } + else if ( pd->curLocalFrame->objFieldMap->find( name ) != 0 ) + return true; + else if ( pd->curLocalFrame->objMethodMap->find( name ) != 0 ) + return true; + + return false; +} + +void LangVarRef::loadObj( ParseData *pd, CodeVect &code, + int lastPtrInQual, bool forWriting ) const +{ + if ( isLocalRef( pd ) ) + loadLocalObj( pd, code, lastPtrInQual, forWriting ); + else + loadGlobalObj( pd, code, lastPtrInQual, forWriting ); +} + +VarRefLookup LangVarRef::lookupQualification( ParseData *pd, ObjectDef *rootDef ) const +{ + int lastPtrInQual = -1; + ObjectDef *searchObjDef = rootDef; + int firstConstPart = -1; + + for ( QualItemVect::Iter qi = *qual; qi.lte(); qi++ ) { + /* Lookup the field int the current qualification. */ + ObjFieldMapEl *objDefMapEl = searchObjDef->objFieldMap->find( qi->data ); + if ( objDefMapEl == 0 ) + error(qi->loc) << "cannot resolve qualification " << qi->data << endp; + ObjField *el = objDefMapEl->value; + + /* Lookup the type of the field. */ + UniqueType *qualUT = el->typeRef->lookupType( pd ); + + /* If we are dealing with an iterator then dereference it. */ + if ( qualUT->typeId == TYPE_ITER ) + qualUT = el->typeRef->searchTypeRef->lookupType( pd ); + + /* Is it const? */ + if ( firstConstPart < 0 && el->isConst ) + firstConstPart = qi.pos(); + + /* Check for references. When loop is done we will have the last one + * present, if any. */ + if ( qualUT->typeId == TYPE_PTR ) + lastPtrInQual = qi.pos(); + + if ( qi->type == QualItem::Dot ) { + /* Cannot dot a reference. Iterator yes (access of the iterator + * not the current) */ + if ( qualUT->typeId == TYPE_PTR ) + error(loc) << "dot cannot be used to access a pointer" << endp; + } + else if ( qi->type == QualItem::Arrow ) { + if ( qualUT->typeId == TYPE_ITER ) + qualUT = el->typeRef->searchTypeRef->lookupType( pd ); + else if ( qualUT->typeId == TYPE_PTR ) + qualUT = pd->findUniqueType( TYPE_TREE, qualUT->langEl ); + } + + searchObjDef = objDefFromUT( pd, qualUT ); + } + + return VarRefLookup( lastPtrInQual, firstConstPart, searchObjDef ); +} + +VarRefLookup LangVarRef::lookupObj( ParseData *pd ) const +{ + ObjectDef *rootDef; + if ( isLocalRef( pd ) ) + rootDef = pd->curLocalFrame; + else + rootDef = pd->globalObjectDef; + + return lookupQualification( pd, rootDef ); +} + +VarRefLookup LangVarRef::lookupField( ParseData *pd ) const +{ + /* Lookup the object that the field is in. */ + VarRefLookup lookup = lookupObj( pd ); + + /* Lookup the field. */ + ObjFieldMapEl *objDefMapEl = lookup.inObject->objFieldMap->find( name ); + if ( objDefMapEl == 0 ) + error(loc) << "cannot find name " << name << " in object" << endp; + + ObjField *field = objDefMapEl->value; + + lookup.objField = field; + lookup.uniqueType = field->typeRef->lookupType( pd ); + + if ( field->typeRef->searchTypeRef != 0 ) + lookup.iterSearchUT = field->typeRef->searchTypeRef->lookupType( pd ); + + return lookup; +} + +VarRefLookup LangVarRef::lookupMethod( ParseData *pd ) const +{ + /* Lookup the object that the field is in. */ + VarRefLookup lookup = lookupObj( pd ); + + /* Find the method. */ + assert( lookup.inObject->objMethodMap != 0 ); + ObjMethod *method = lookup.inObject->findMethod( name ); + if ( method == 0 ) + error(loc) << "cannot find " << name << "(...) in object" << endp; + + lookup.objMethod = method; + lookup.uniqueType = method->returnUT; + + return lookup; +} + +void LangVarRef::setFieldInstr( ParseData *pd, CodeVect &code, + ObjectDef *inObject, ObjField *el, UniqueType *exprUT, bool revert ) const +{ + /* Ensure that the field is referenced. */ + inObject->referenceField( pd, el ); + + if ( pd->revertOn && revert ) + code.append( el->inSetWV ); + else + code.append( el->inSetWC ); + + /* Maybe write out an offset. */ + if ( el->useOffset ) + code.appendHalf( el->offset ); +} + +bool castAssignment( ParseData *pd, CodeVect &code, UniqueType *destUT, + UniqueType *destSearchUT, UniqueType *srcUT ) +{ + if ( destUT == srcUT ) + return true; + + /* Casting trees to any. */ + if ( destUT->typeId == TYPE_TREE && destUT->langEl == pd->anyKlangEl && + srcUT->typeId == TYPE_TREE ) + return true; + + /* Setting a reference from a tree. */ + if ( destUT->typeId == TYPE_REF && srcUT->typeId == TYPE_TREE && + destUT->langEl == srcUT->langEl ) + return true; + + /* Setting an iterator from a tree. */ + if ( destUT->typeId == TYPE_ITER && srcUT->typeId == TYPE_TREE && + destSearchUT->langEl == srcUT->langEl ) + return true; + + /* Assigning nil to a tree. */ + if ( destUT->typeId == TYPE_TREE && srcUT->typeId == TYPE_NIL ) + return true; + + /* Assigning nil to a pointer. */ + if ( destUT->typeId == TYPE_PTR && srcUT->typeId == TYPE_NIL ) + return true; + + return false; +} + +void LangVarRef::setField( ParseData *pd, CodeVect &code, + ObjectDef *inObject, UniqueType *exprUT, bool revert ) const +{ + ObjFieldMapEl *objDefMapEl = inObject->objFieldMap->find( name ); + if ( objDefMapEl == 0 ) + error(loc) << "cannot find name " << name << " in object" << endp; + + ObjField *el = objDefMapEl->value; + setFieldInstr( pd, code, inObject, el, exprUT, revert ); +} + +void LangVarRef::setFieldIter( ParseData *pd, CodeVect &code, + ObjectDef *inObject, UniqueType *objUT, UniqueType *exprType, bool revert ) const +{ + ObjFieldMapEl *objDefMapEl = inObject->objFieldMap->find( name ); + if ( objDefMapEl == 0 ) + error(loc) << "cannot find name " << name << " in object" << endp; + + ObjField *el = objDefMapEl->value; + code.append( objUT->iterDef->inSetCurWC ); + code.appendHalf( el->offset ); +} + +UniqueType *LangVarRef::evaluate( ParseData *pd, CodeVect &code, bool forWriting ) const +{ + /* Lookup the loadObj. */ + VarRefLookup lookup = lookupField( pd ); + + /* Load the object, if any. */ + loadObj( pd, code, lookup.lastPtrInQual, forWriting ); + + /* Load the field. */ + UniqueType *ut = loadFieldInstr( pd, code, lookup.inObject, + lookup.objField, forWriting, false ); + + return ut; +} + +void LangVarRef::canTakeRef( ParseData *pd, VarRefLookup &lookup ) const +{ + bool canTake = false; + + /* If the var is not a local, it must be an attribute accessed + * via a local and attributes. */ + if ( lookup.inObject->type == ObjectDef::FrameType ) + canTake = true; + else if ( isLocalRef(pd) && lookup.lastPtrInQual < 0 && lookup.uniqueType->typeId != TYPE_PTR ) + canTake = true; + + if ( !canTake ) { + error(loc) << "can only take references of locals or " + "attributes accessed via a local" << endp; + } + + if ( lookup.objField->refActive ) + error(loc) << "reference currently active, cannot take another" << endp; +} + +/* Return the field referenced. */ +ObjField *LangVarRef::preEvaluateRef( ParseData *pd, CodeVect &code ) const +{ + /* Lookup the loadObj. */ + VarRefLookup lookup = lookupField( pd ); + + canTakeRef( pd, lookup ); + + loadQualificationRefs( pd, code ); + + return lookup.objField; +} + +/* Return the field referenced. */ +ObjField *LangVarRef::evaluateRef( ParseData *pd, CodeVect &code, long pushCount ) const +{ + /* Lookup the loadObj. */ + VarRefLookup lookup = lookupField( pd ); + + canTakeRef( pd, lookup ); + + /* Ensure that the field is referenced. */ + lookup.inObject->referenceField( pd, lookup.objField ); + + /* Note that we could have modified children. */ + if ( qual->length() == 0 ) + lookup.objField->refActive = true; + + /* Whenever we take a reference we have to assume writing and that the + * tree is dirty. */ + lookup.objField->dirtyTree = true; + + if ( qual->length() > 0 ) { + code.append( IN_REF_FROM_QUAL_REF ); + code.appendHalf( pushCount ); + code.appendHalf( lookup.objField->offset ); + } + else if ( lookup.objField->typeRef->iterDef != 0 ) { + code.append( lookup.objField->typeRef->iterDef->inRefFromCur ); + code.appendHalf( lookup.objField->offset ); + } + else if ( lookup.objField->typeRef->isRef ) { + code.append( IN_REF_FROM_REF ); + code.appendHalf( lookup.objField->offset ); + } + else { + code.append( IN_REF_FROM_LOCAL ); + code.appendHalf( lookup.objField->offset ); + } + + return lookup.objField; +} + +ObjField **LangVarRef::evaluateArgs( ParseData *pd, CodeVect &code, + VarRefLookup &lookup, ExprVect *args ) const +{ + /* Parameter list is given only for user defined methods. Otherwise it + * will be null. */ + ParameterList *paramList = lookup.objMethod->paramList; + + /* Match the number of arguments. */ + int numArgs = args != 0 ? args->length() : 0; + if ( numArgs != lookup.objMethod->numParams ) + error(loc) << "wrong number of arguments" << endp; + + /* This is for storing the object fields used by references. */ + ObjField **paramRefs = new ObjField*[numArgs]; + memset( paramRefs, 0, sizeof(ObjField*) * numArgs ); + + /* Evaluate and push the args. */ + if ( args != 0 ) { + /* We use this only if there is a paramter list. */ + ParameterList::Iter p; + long pushCount = 0; + + /* First pass we need to push object loads for reference parameters. */ + paramList != 0 && ( p = *paramList ); + for ( ExprVect::Iter pe = *args; pe.lte(); pe++ ) { + /* Get the expression and the UT for the arg. */ + LangExpr *expression = *pe; + UniqueType *paramUT = lookup.objMethod->paramUTs[pe.pos()]; + + if ( paramUT->typeId == TYPE_REF ) { + /* Make sure we are dealing with a variable reference. */ + if ( expression->type != LangExpr::TermType ) + error(loc) << "not a term: argument must be a local variable" << endp; + if ( expression->term->type != LangTerm::VarRefType ) + error(loc) << "not a variable: argument must be a local variable" << endp; + + /* Lookup the field. */ + LangVarRef *varRef = expression->term->varRef; + + ObjField *refOf = varRef->preEvaluateRef( pd, code ); + paramRefs[pe.pos()] = refOf; + + pushCount += varRef->qual->length() * 2; + } + + /* Advance the parameter list iterator if we have it. */ + paramList != 0 && p.increment(); + } + + paramList != 0 && ( p = *paramList ); + for ( ExprVect::Iter pe = *args; pe.lte(); pe++ ) { + /* Get the expression and the UT for the arg. */ + LangExpr *expression = *pe; + UniqueType *paramUT = lookup.objMethod->paramUTs[pe.pos()]; + + if ( paramUT->typeId == TYPE_REF ) { + + /* Make sure we are dealing with a variable reference. */ + if ( expression->type != LangExpr::TermType ) + error(loc) << "not a term: argument must be a local variable" << endp; + if ( expression->term->type != LangTerm::VarRefType ) + error(loc) << "not a variable: argument must be a local variable" << endp; + + /* Lookup the field. */ + LangVarRef *varRef = expression->term->varRef; + + pushCount -= varRef->qual->length() * 2; + + ObjField *refOf = varRef->evaluateRef( pd, code, pushCount ); + paramRefs[pe.pos()] = refOf; + + pushCount += 2; + } + else { + UniqueType *exprUT = expression->evaluate( pd, code ); + + if ( !castAssignment( pd, code, paramUT, 0, exprUT ) ) + error(loc) << "arg " << pe.pos()+1 << " is of the wrong type" << endp; + + pushCount += 1; + } + + /* Advance the parameter list iterator if we have it. */ + paramList != 0 && p.increment(); + } + } + + return paramRefs; +} + +void LangVarRef::resetActiveRefs( ParseData *pd, VarRefLookup &lookup, ObjField **paramRefs ) const +{ + /* Parameter list is given only for user defined methods. Otherwise it + * will be null. */ + for ( long p = 0; p < lookup.objMethod->numParams; p++ ) { + if ( paramRefs[p] != 0 ) + paramRefs[p]->refActive = false; + } +} + + +void LangVarRef::callOperation( ParseData *pd, CodeVect &code, VarRefLookup &lookup ) const +{ + /* This is for writing if it is a non-const builtin. */ + bool forWriting = lookup.objMethod->func == 0 && + !lookup.objMethod->isConst; + + if ( lookup.objMethod->useCallObj ) { + /* Load the object, if any. */ + loadObj( pd, code, lookup.lastPtrInQual, forWriting ); + } + + /* Check if we need to revert the function. If it operates on a reference + * or if it is not local then we need to revert it. */ + bool revert = lookup.lastPtrInQual >= 0 || !isLocalRef(pd); + + /* The call instruction. */ + if ( pd->revertOn && revert ) + code.append( lookup.objMethod->opcodeWV ); + else + code.append( lookup.objMethod->opcodeWC ); + + if ( lookup.objMethod->useFuncId ) + code.appendHalf( lookup.objMethod->funcId ); +} + +void LangVarRef::popRefQuals( ParseData *pd, CodeVect &code, + VarRefLookup &lookup, ExprVect *args ) const +{ + long popCount = 0; + + /* Evaluate and push the args. */ + if ( args != 0 ) { + /* We use this only if there is a paramter list. */ + for ( ExprVect::Iter pe = *args; pe.lte(); pe++ ) { + /* Get the expression and the UT for the arg. */ + LangExpr *expression = *pe; + UniqueType *paramUT = lookup.objMethod->paramUTs[pe.pos()]; + + if ( paramUT->typeId == TYPE_REF ) { + /* Lookup the field. */ + LangVarRef *varRef = expression->term->varRef; + popCount += varRef->qual->length() * 2; + } + } + if ( popCount > 0 ) { + code.append( IN_POP_N_WORDS ); + code.appendHalf( (short)popCount ); + } + } +} + +UniqueType *LangVarRef::evaluateCall( ParseData *pd, CodeVect &code, ExprVect *args ) const +{ + /* Evaluate the object. */ + VarRefLookup lookup = lookupMethod( pd ); + + /* Evaluate and push the arguments. */ + ObjField **paramRefs = evaluateArgs( pd, code, lookup, args ); + + /* Write the call opcode. */ + callOperation( pd, code, lookup ); + + popRefQuals( pd, code, lookup, args ); + + resetActiveRefs( pd, lookup, paramRefs); + delete[] paramRefs; + + /* Return the type to the expression. */ + return lookup.uniqueType; +} + +UniqueType *LangTerm::evaluateMatch( ParseData *pd, CodeVect &code ) const +{ + /* Add the vars bound by the pattern into the local scope. */ + for ( PatternItemList::Iter item = *pattern->list; item.lte(); item++ ) { + if ( item->varRef != 0 ) + item->bindId = pattern->nextBindId++; + } + + UniqueType *ut = varRef->evaluate( pd, code ); + if ( ut->typeId != TYPE_TREE ) + error(varRef->loc) << "expected match against a tree type" << endp; + + /* Store the language element type in the pattern. This is needed by + * the pattern parser. */ + pattern->langEl = ut->langEl; + + code.append( IN_MATCH ); + code.appendHalf( pattern->patRepId ); + + for ( PatternItemList::Iter item = pattern->list->last(); item.gtb(); item-- ) { + if ( item->varRef != 0 ) { + /* Compute the unique type. */ + UniqueType *exprType = pd->findUniqueType( TYPE_TREE, item->factor->langEl ); + + /* Get the type of the variable being assigned to. */ + VarRefLookup lookup = item->varRef->lookupField( pd ); + + item->varRef->loadObj( pd, code, lookup.lastPtrInQual, false ); + item->varRef->setField( pd, code, lookup.inObject, exprType, false ); + } + } + + return ut; +} + +UniqueType *LangTerm::evaluateNew( ParseData *pd, CodeVect &code ) const +{ + /* Evaluate the expression. */ + UniqueType *ut = expr->evaluate( pd, code ); + if ( ut->typeId != TYPE_TREE ) + error() << "new can only be applied to tree types" << endp; + + code.append( IN_TREE_NEW ); + return pd->findUniqueType( TYPE_PTR, ut->langEl ); +} + +void LangTerm::assignFieldArgs( ParseData *pd, CodeVect &code, UniqueType *replUT ) const +{ + /* Now assign the field initializations. Note that we need to do this in + * reverse because the last expression evaluated is at the top of the + * stack. */ + if ( fieldInitArgs != 0 && fieldInitArgs->length() > 0 ) { + ObjectDef *objDef = objDefFromUT( pd, replUT ); + /* Note the reverse traversal. */ + for ( FieldInitVect::Iter pi = fieldInitArgs->last(); pi.gtb(); pi-- ) { + FieldInit *fieldInit = *pi; + ObjFieldMapEl *el = objDef->objFieldMap->find( fieldInit->name ); + if ( el == 0 ) { + error(fieldInit->loc) << "failed to find init name " << + fieldInit->name << " in object" << endp; + } + + /* Lookup the type of the field and compare it to the type of the + * expression. */ + ObjField *field = el->value; + UniqueType *fieldUT = field->typeRef->lookupType( pd ); + if ( !castAssignment( pd, code, fieldUT, 0, fieldInit->exprUT ) ) + error(fieldInit->loc) << "type mismatch in initialization" << endp; + + /* The set field instruction must leave the object on the top of + * the stack. */ + code.append( IN_SET_FIELD_LEAVE_WC ); + code.appendHalf( field->offset ); + } + } +} + +UniqueType *LangTerm::evaluateTreeConstruct( ParseData *pd, CodeVect &code ) const +{ + /* Evaluate the initialization expressions. */ + if ( fieldInitArgs != 0 && fieldInitArgs->length() > 0 ) { + for ( FieldInitVect::Iter pi = *fieldInitArgs; pi.lte(); pi++ ) { + FieldInit *fieldInit = *pi; + fieldInit->exprUT = fieldInit->expr->evaluate( pd, code ); + } + } + + /* Assign bind ids to the variables in the replacement. */ + for ( ReplItemList::Iter item = *replacement->list; item.lte(); item++ ) { + if ( item->varRef != 0 ) + item->bindId = replacement->nextBindId++; + } + + /* Evaluate variable references. */ + for ( ReplItemList::Iter item = replacement->list->last(); item.gtb(); item-- ) { + if ( item->type == ReplItem::VarRefType ) { + UniqueType *ut = item->varRef->evaluate( pd, code ); + + if ( ut->typeId != TYPE_TREE ) + error() << "variables used in replacements must be trees" << endp; + + item->langEl = ut->langEl; + } + } + + /* Construct the tree using the tree information stored in the compiled + * code. */ + code.append( IN_CONSTRUCT ); + code.appendHalf( replacement->patRepId ); + + /* Lookup the type of the replacement and store it in the replacement + * object so that replacement parsing has a target. */ + UniqueType *replUT = typeRef->lookupType( pd ); + if ( replUT->typeId == TYPE_TREE ) + replacement->langEl = replUT->langEl; + else + error(loc) << "don't know how to construct this type" << endp; + + assignFieldArgs( pd, code, replUT ); + + return replUT; +} + + +UniqueType *LangTerm::evaluateTermConstruct( ParseData *pd, CodeVect &code ) const +{ + /* Going to make this replacement directly. Take it out of the list of + * replacements so that we don't try to parse it. */ + pd->replList.remove( replacement ); + + /* Evaluate the initialization expressions. */ + if ( fieldInitArgs != 0 && fieldInitArgs->length() > 0 ) { + for ( FieldInitVect::Iter pi = *fieldInitArgs; pi.lte(); pi++ ) { + FieldInit *fieldInit = *pi; + fieldInit->exprUT = fieldInit->expr->evaluate( pd, code ); + } + } + + UniqueType *replUT = typeRef->lookupType( pd ); + + /* Evaluate the expression that we are constructing the term with and make + * the term. */ + ReplItem *replItem = replacement->list->head; + replItem->varRef->evaluate( pd, code ); + code.append( IN_CONSTRUCT_TERM ); + code.appendHalf( replUT->langEl->id ); + + assignFieldArgs( pd, code, replUT ); + return replUT; +} + +bool LangTerm::constructTermFromString( ParseData *pd ) const +{ + UniqueType *replUT = typeRef->lookupType( pd ); + if ( replUT->typeId == TYPE_TREE && replUT->langEl->id < pd->firstNonTermId ) { + if ( replacement->list->length() == 1 ) { + ReplItem *replItem = replacement->list->head; + if ( replItem->type == ReplItem::VarRefType ) { + VarRefLookup lookup = replItem->varRef->lookupField( pd ); + if ( lookup.uniqueType == pd->uniqueTypeStr ) + return true; + } + } + } + return false; +} + +UniqueType *LangTerm::evaluateConstruct( ParseData *pd, CodeVect &code ) const +{ + /* If the type is a token and the replacement contains just a string then + * construct a token using the text of the string. Otherwise do a normal + * tree construct. */ + if ( constructTermFromString( pd ) ) + return evaluateTermConstruct( pd, code ); + else + return evaluateTreeConstruct( pd, code ); +} + +UniqueType *LangTerm::evaluateParse( ParseData *pd, CodeVect &code, bool stop ) const +{ + UniqueType *ut = typeRef->lookupType( pd ); + if ( ut->typeId != TYPE_TREE ) + error(loc) << "can only parse trees" << endl; + + /* Should be one arg, a stream. */ + if ( args == 0 || args->length() != 1 ) + error(loc) << "expecting one argument" << endp; + + UniqueType *argUT = args->data[0]->evaluate( pd, code ); + if ( argUT != pd->uniqueTypeStream ) + error(loc) << "single argument must be a stream" << endp; + + /* Allocate a parser id. This will cause a parser to be built for + * the type. */ + ut->langEl->parserId = pd->nextParserId++; + if ( stop ) + ut->langEl->parseStop = true; + + /* Parse instruction, dependent on whether or not we are + * producing revert or commit code. */ + if ( pd->revertOn ) + code.append( IN_PARSE_WV ); + else + code.append( IN_PARSE_WC ); + + /* The id of the parser, followed by the stop id. */ + code.appendHalf( ut->langEl->parserId ); + if ( stop ) + code.appendHalf( ut->langEl->id ); + else + code.appendHalf( 0 ); + + return ut; +} + +UniqueType *LangTerm::evaluate( ParseData *pd, CodeVect &code ) const +{ + switch ( type ) { + case VarRefType: + return varRef->evaluate( pd, code ); + case MethodCallType: + return varRef->evaluateCall( pd, code, args ); + case NilType: + code.append( IN_LOAD_NIL ); + return pd->uniqueTypeNil; + case TrueType: + code.append( IN_LOAD_TRUE ); + return pd->uniqueTypeBool; + case FalseType: + code.append( IN_LOAD_FALSE ); + return pd->uniqueTypeBool; + case MakeTokenType: + return evaluateMakeToken( pd, code ); + case MakeTreeType: + return evaluateMakeTree( pd, code ); + case NumberType: { + unsigned int n = atoi( data ); + code.append( IN_LOAD_INT ); + code.appendWord( n ); + return pd->uniqueTypeInt; + } + case StringType: { + String interp; + bool unused; + prepareLitString( interp, unused, data, InputLoc() ); + + /* Make sure we have this string. */ + StringMapEl *mapEl = 0; + if ( pd->literalStrings.insert( interp, &mapEl ) ) + mapEl->value = pd->literalStrings.length()-1; + + code.append( IN_LOAD_STR ); + code.appendWord( mapEl->value ); + return pd->uniqueTypeStr; + } + case MatchType: + return evaluateMatch( pd, code ); + case ParseType: + return evaluateParse( pd, code, false ); + case ParseStopType: + return evaluateParse( pd, code, true ); + case ConstructType: + return evaluateConstruct( pd, code ); + case NewType: + return evaluateNew( pd, code ); + case TypeIdType: { + /* Evaluate the expression. */ + UniqueType *ut = typeRef->lookupType( pd ); + if ( ut->typeId != TYPE_TREE ) + error() << "typeid can only be applied to tree types" << endp; + + code.append( IN_LOAD_INT ); + code.appendWord( ut->langEl->id ); + return pd->uniqueTypeInt; + } + case SearchType: { + /* Evaluate the expression. */ + UniqueType *ut = typeRef->lookupType( pd ); + if ( ut->typeId != TYPE_TREE ) + error(loc) << "can only search for tree types" << endp; + + UniqueType *treeUT = varRef->evaluate( pd, code ); + if ( treeUT->typeId != TYPE_TREE ) + error(loc) << "search can be applied only to tree types" << endl; + + code.append( IN_TREE_SEARCH ); + code.appendWord( ut->langEl->id ); + return ut; + }; + } + return 0; +} + +UniqueType *LangExpr::evaluate( ParseData *pd, CodeVect &code ) const +{ + switch ( type ) { + case BinaryType: { + switch ( op ) { + case '+': { + UniqueType *lt = left->evaluate( pd, code ); + UniqueType *rt = right->evaluate( pd, code ); + + if ( lt == pd->uniqueTypeInt && rt == pd->uniqueTypeInt ) { + code.append( IN_ADD_INT ); + return pd->uniqueTypeInt; + } + + if ( lt == pd->uniqueTypeStr && rt == pd->uniqueTypeStr ) { + code.append( IN_CONCAT_STR ); + return pd->uniqueTypeStr; + } + + error(loc) << "do not have an addition operator for these types" << endp; + break; + } + case '-': { + UniqueType *lt = left->evaluate( pd, code ); + UniqueType *rt = right->evaluate( pd, code ); + + if ( lt == pd->uniqueTypeInt && rt == pd->uniqueTypeInt ) { + code.append( IN_SUB_INT ); + return pd->uniqueTypeInt; + } + + error(loc) << "do not have an addition operator for these types" << endp; + break; + } + case '*': { + UniqueType *lt = left->evaluate( pd, code ); + UniqueType *rt = right->evaluate( pd, code ); + + if ( lt == pd->uniqueTypeInt && rt == pd->uniqueTypeInt ) { + code.append( IN_MULT_INT ); + return pd->uniqueTypeInt; + } + + error(loc) << "do not have an multiplication " + "operator for these types" << endp; + break; + } + case OP_DoubleEql: { + UniqueType *lt = left->evaluate( pd, code ); + UniqueType *rt = right->evaluate( pd, code ); + + if ( lt != rt ) + error(loc) << "comparison of different types" << endp; + + code.append( IN_TST_EQL ); + return pd->uniqueTypeBool; + } + case OP_NotEql: { + UniqueType *lt = left->evaluate( pd, code ); + UniqueType *rt = right->evaluate( pd, code ); + + if ( lt != rt ) + error(loc) << "comparison of different types" << endp; + + code.append( IN_TST_NOT_EQL ); + return pd->uniqueTypeBool; + } + case '<': { + left->evaluate( pd, code ); + right->evaluate( pd, code ); + + code.append( IN_TST_LESS ); + return pd->uniqueTypeBool; + } + case '>': { + left->evaluate( pd, code ); + right->evaluate( pd, code ); + + code.append( IN_TST_GRTR ); + return pd->uniqueTypeBool; + } + case OP_LessEql: { + left->evaluate( pd, code ); + right->evaluate( pd, code ); + + code.append( IN_TST_LESS_EQL ); + return pd->uniqueTypeBool; + } + case OP_GrtrEql: { + left->evaluate( pd, code ); + right->evaluate( pd, code ); + + code.append( IN_TST_GRTR_EQL ); + return pd->uniqueTypeBool; + } + case OP_LogicalAnd: { + /* Evaluate the left and duplicate it. */ + left->evaluate( pd, code ); + code.append( IN_DUP_TOP ); + + /* Jump over the right if false, leaving the original left + * result on the top of the stack. We don't know the + * distance yet so record the position of the jump. */ + long jump = code.length(); + code.append( IN_JMP_FALSE ); + code.appendHalf( 0 ); + + /* Evauluate the right, add the test. Store it separately. */ + right->evaluate( pd, code ); + code.append( IN_TST_LOGICAL_AND ); + + /* Set the distance of the jump. */ + long distance = code.length() - jump - 3; + code.setHalf( jump+1, distance ); + + return pd->uniqueTypeInt; + } + case OP_LogicalOr: { + /* Evaluate the left and duplicate it. */ + left->evaluate( pd, code ); + code.append( IN_DUP_TOP ); + + /* Jump over the right if true, leaving the original left + * result on the top of the stack. We don't know the + * distance yet so record the position of the jump. */ + long jump = code.length(); + code.append( IN_JMP_TRUE ); + code.appendHalf( 0 ); + + /* Evauluate the right, add the test. */ + right->evaluate( pd, code ); + code.append( IN_TST_LOGICAL_OR ); + + /* Set the distance of the jump. */ + long distance = code.length() - jump - 3; + code.setHalf( jump+1, distance ); + + return pd->uniqueTypeInt; + } + } + + assert(false); + return 0; + } + case UnaryType: { + switch ( op ) { + case '!': { + /* Evaluate the left and duplicate it. */ + right->evaluate( pd, code ); + code.append( IN_NOT ); + return pd->uniqueTypeBool; + } + case OP_Deref: { + UniqueType *ut = right->evaluate( pd, code ); + if ( ut->typeId != TYPE_PTR ) + error(loc) << "can only dereference pointers" << endl; + + code.append( IN_PTR_DEREF_R ); + ut = pd->findUniqueType( TYPE_TREE, ut->langEl ); + return ut; + } + default: + assert(false); + } + return 0; + } + case TermType: { + return term->evaluate( pd, code ); + } + } + return 0; +} + +void LangVarRef::assignValue( ParseData *pd, CodeVect &code, + UniqueType *exprUT ) const +{ + /* Lookup the left hand side of the assignment. */ + VarRefLookup lookup = lookupField( pd ); + + if ( lookup.objField->refActive ) + error(loc) << "reference active, cannot write to object" << endp; + + if ( lookup.firstConstPart >= 0 ) { + error(loc) << "left hand side qualification \"" << + qual->data[lookup.firstConstPart].data << "\" is const" << endp; + } + + if ( lookup.objField->isConst ) + error(loc) << "field \"" << name << "\" is const" << endp; + + /* Writing guarantees the field is dirty. tree is dirty. */ + lookup.objField->dirtyTree = true; + + /* Check the types of the assignment and possibly cast. */ + UniqueType *objUT = lookup.objField->typeRef->lookupType( pd ); + assert( lookup.uniqueType == lookup.objField->typeRef->lookupType( pd ) ); + if ( !castAssignment( pd, code, objUT, lookup.iterSearchUT, exprUT ) ) + error(loc) << "type mismatch in assignment" << endp; + + /* Decide if we need to revert the assignment. */ + bool revert = lookup.lastPtrInQual >= 0 || !isLocalRef(pd); + + /* Load the object and generate the field setting code. */ + loadObj( pd, code, lookup.lastPtrInQual, true ); + + if ( lookup.uniqueType->typeId == TYPE_ITER ) + setFieldIter( pd, code, lookup.inObject, lookup.uniqueType, exprUT, false ); + else + setField( pd, code, lookup.inObject, exprUT, revert ); +} + +UniqueType *LangTerm::evaluateMakeToken( ParseData *pd, CodeVect &code ) const +{ +// if ( pd->compileContext != ParseData::CompileTranslation ) +// error(loc) << "make_token can be used only in a translation block" << endp; + + /* Match the number of arguments. */ + int numArgs = args != 0 ? args->length() : 0; + if ( numArgs < 2 ) + error(loc) << "need at least two arguments" << endp; + + for ( ExprVect::Iter pe = *args; pe.lte(); pe++ ) { + /* Evaluate. */ + UniqueType *exprUT = (*pe)->evaluate( pd, code ); + + if ( pe.pos() == 0 && exprUT != pd->uniqueTypeInt ) + error(loc) << "first arg, id, must be an int" << endp; + + if ( pe.pos() == 1 && exprUT != pd->uniqueTypeStr ) + error(loc) << "second arg, length, must be a string" << endp; + } + + /* The token is now created, send it. */ + code.append( IN_MAKE_TOKEN ); + code.append( args->length() ); + + return pd->uniqueTypeAny; +} + +UniqueType *LangTerm::evaluateMakeTree( ParseData *pd, CodeVect &code ) const +{ + if ( pd->compileContext != ParseData::CompileTranslation ) + error(loc) << "make_tree can be used only in a translation block" << endp; + + /* Match the number of arguments. */ + int numArgs = args != 0 ? args->length() : 0; + if ( numArgs < 1 ) + error(loc) << "need at least one argument" << endp; + + for ( ExprVect::Iter pe = *args; pe.lte(); pe++ ) { + /* Evaluate. */ + UniqueType *exprUT = (*pe)->evaluate( pd, code ); + + if ( pe.pos() == 0 && exprUT != pd->uniqueTypeInt ) + error(loc) << "first arg, nonterm id, must be an int" << endp; + } + + /* The token is now created, send it. */ + code.append( IN_MAKE_TREE ); + code.append( args->length() ); + + return pd->uniqueTypeAny; +} + +void LangStmt::compileForIterBody( ParseData *pd, + CodeVect &code, UniqueType *iterUT ) const +{ + /* Remember the top of the loop. */ + long top = code.length(); + + /* Advance */ + code.append( iterUT->iterDef->inAdvance ); + code.appendHalf( objField->offset ); + + /* Test: jump past the while block if false. Note that we don't have the + * distance yet. */ + long jumpFalse = code.length(); + code.append( IN_JMP_FALSE ); + code.appendHalf( 0 ); + + /* + * Set up the loop cleanup code. + */ + + /* Set up the current loop cleanup. */ + CodeVect loopCleanup; + if ( pd->loopCleanup != 0 ) + loopCleanup.setAs( *pd->loopCleanup ); + + /* Add the cleanup for the current loop. */ + loopCleanup.append( iterUT->iterDef->inDestroy ); + loopCleanup.appendHalf( objField->offset ); + + /* Push the loop cleanup. */ + CodeVect *oldLoopCleanup = pd->loopCleanup; + pd->loopCleanup = &loopCleanup; + + /* Compile the contents. */ + for ( StmtList::Iter stmt = *stmtList; stmt.lte(); stmt++ ) + stmt->compile( pd, code ); + + pd->loopCleanup = oldLoopCleanup; + + /* Jump back to the top to retest. */ + long retestDist = code.length() - top + 3; + code.append( IN_JMP ); + code.appendHalf( -retestDist ); + + /* Set the jump false distance. */ + long falseDist = code.length() - jumpFalse - 3; + code.setHalf( jumpFalse+1, falseDist ); + + /* Compute the jump distance for the break jumps. */ + for ( LongVect::Iter brk = pd->breakJumps; brk.lte(); brk++ ) { + long distance = code.length() - *brk - 3; + code.setHalf( *brk+1, distance ); + } + pd->breakJumps.empty(); + + /* Destroy the iterator. */ + code.append( iterUT->iterDef->inDestroy ); + code.appendHalf( objField->offset ); + + /* Clean up any prepush args. */ +} + +LangTerm *LangStmt::chooseDefaultIter( ParseData *pd, LangTerm *fromVarRef ) const +{ + /* Lookup the lang term and decide what iterator to use based + * on its type. */ + VarRefLookup lookup = fromVarRef->varRef->lookupField( pd ); + + if ( lookup.inObject->type != ObjectDef::FrameType ) + error(loc) << "root of iteration must be a local" << endp; + + LangVarRef *callVarRef = 0; + if ( lookup.uniqueType->typeId == TYPE_TREE || + lookup.uniqueType->typeId == TYPE_REF || + lookup.uniqueType->typeId == TYPE_ITER || + lookup.uniqueType->typeId == TYPE_PTR ) + { + /* The iterator name. */ + callVarRef = new LangVarRef( loc, new QualItemVect, "triter" ); + } + else { + error(loc) << "there is no default iterator for a " + "root of that type" << endp; + } + + /* The parameters. */ + ExprVect *callExprVect = new ExprVect; + LangExpr *callExpr = new LangExpr( new LangTerm( + LangTerm::VarRefType, fromVarRef->varRef ) ); + callExprVect->append( callExpr ); + + LangTerm *callLangTerm = new LangTerm( callVarRef, callExprVect ); + + return callLangTerm; +} + +void LangStmt::compileForIter( ParseData *pd, CodeVect &code ) const +{ + LangTerm *iterCallTerm = langTerm; + if ( iterCallTerm->type != LangTerm::MethodCallType ) + iterCallTerm = chooseDefaultIter( pd, langTerm ); + + /* The type we are searching for. */ + UniqueType *searchUT = typeRef->lookupType( pd ); + + /* + * Declare the iterator variable. + */ + VarRefLookup lookup = iterCallTerm->varRef->lookupMethod( pd ); + if ( lookup.objMethod->iterDef == 0 ) { + error(loc) << "attempt to iterate using something " + "that is not an iterator" << endp; + } + + /* Now that we have done the iterator call lookup we can make the type + * reference for the object field. */ + TypeRef *iterTypeRef = new TypeRef( loc, lookup.objMethod->iterDef, typeRef ); + objField->typeRef = iterTypeRef; + + /* Also force the field to be initialized. */ + pd->curLocalFrame->initField( pd, objField ); + + /* + * Create the iterator from the local var. + */ + + UniqueType *iterUT = iterTypeRef->lookupType( pd ); + + /* Evaluate and push the arguments. */ + ObjField **paramRefs = iterCallTerm->varRef->evaluateArgs( + pd, code, lookup, iterCallTerm->args ); + + if ( pd->revertOn ) + code.append( iterUT->iterDef->inCreateWV ); + else + code.append( iterUT->iterDef->inCreateWC ); + + code.appendHalf( objField->offset ); + if ( lookup.objMethod->func != 0 ) + code.appendHalf( lookup.objMethod->func->funcId ); + + if ( iterUT->iterDef->useSearchUT ) { + if ( searchUT->typeId == TYPE_PTR ) + code.appendHalf( pd->uniqueTypePtr->langEl->id ); + else + code.appendHalf( searchUT->langEl->id ); + } + + compileForIterBody( pd, code, iterUT ); + + iterCallTerm->varRef->popRefQuals( pd, code, lookup, iterCallTerm->args ); + + iterCallTerm->varRef->resetActiveRefs( pd, lookup, paramRefs ); + delete[] paramRefs; +} + +void LangStmt::compileWhile( ParseData *pd, CodeVect &code ) const +{ + /* Generate code for the while test. Remember the top. */ + long top = code.length(); + expr->evaluate( pd, code ); + + /* Jump past the while block if false. Note that we don't have the + * distance yet. */ + long jumpFalse = code.length(); + code.append( IN_JMP_FALSE ); + code.appendHalf( 0 ); + + /* Compute the while block. */ + for ( StmtList::Iter stmt = *stmtList; stmt.lte(); stmt++ ) + stmt->compile( pd, code ); + + /* Jump back to the top to retest. */ + long retestDist = code.length() - top + 3; + code.append( IN_JMP ); + code.appendHalf( -retestDist ); + + /* Set the jump false distance. */ + long falseDist = code.length() - jumpFalse - 3; + code.setHalf( jumpFalse+1, falseDist ); + + /* Compute the jump distance for the break jumps. */ + for ( LongVect::Iter brk = pd->breakJumps; brk.lte(); brk++ ) { + long distance = code.length() - *brk - 3; + code.setHalf( *brk+1, distance ); + } + pd->breakJumps.empty(); +} + +void LangStmt::compile( ParseData *pd, CodeVect &code ) const +{ + switch ( type ) { + case PrintType: + case PrintXMLACType: + case PrintXMLType: { + UniqueType **types = new UniqueType*[exprPtrVect->length()]; + + /* Push the args backwards. */ + for ( ExprVect::Iter pex = exprPtrVect->last(); pex.gtb(); pex-- ) + types[pex.pos()] = (*pex)->evaluate( pd, code ); + + /* Run the printing forwards. */ + if ( type == PrintType ) { + for ( ExprVect::Iter pex = *exprPtrVect; pex.lte(); pex++ ) + code.append( IN_PRINT ); + } + else if ( type == PrintXMLACType ) { + for ( ExprVect::Iter pex = *exprPtrVect; pex.lte(); pex++ ) + code.append( IN_PRINT_XML_AC ); + } + else if ( type == PrintXMLType ) { + for ( ExprVect::Iter pex = *exprPtrVect; pex.lte(); pex++ ) + code.append( IN_PRINT_XML ); + } + + delete[] types; + + break; + } + case ExprType: { + /* Evaluate the exrepssion, then pop it immediately. */ + expr->evaluate( pd, code ); + code.append( IN_POP ); + break; + } + case IfType: { + long jumpFalse, jumpPastElse, distance; + + /* Evaluate the test. */ + expr->evaluate( pd, code ); + + /* Jump past the if block if false. We don't know the distance + * yet so store the location of the jump. */ + jumpFalse = code.length(); + code.append( IN_JMP_FALSE ); + code.appendHalf( 0 ); + + /* Compile the if true branch. */ + for ( StmtList::Iter stmt = *stmtList; stmt.lte(); stmt++ ) + stmt->compile( pd, code ); + + if ( elsePart != 0 ) { + /* Jump past the else code for the if true branch. */ + jumpPastElse = code.length(); + code.append( IN_JMP ); + code.appendHalf( 0 ); + } + + /* Set the distance for the jump false case. */ + distance = code.length() - jumpFalse - 3; + code.setHalf( jumpFalse+1, distance ); + + if ( elsePart != 0 ) { + /* Compile the else branch. */ + for ( StmtList::Iter stmt = *elsePart; stmt.lte(); stmt++ ) + stmt->compile( pd, code ); + + /* Set the distance for jump over the else part. */ + distance = code.length() - jumpPastElse - 3; + code.setHalf( jumpPastElse+1, distance ); + } + + break; + } + case RejectType: { + code.append( IN_REJECT ); + break; + } + case WhileType: { + compileWhile( pd, code ); + break; + } + case AssignType: { + /* Evaluate the exrepssion. */ + UniqueType *exprUT = expr->evaluate( pd, code ); + + /* Do the assignment. */ + varRef->assignValue( pd, code, exprUT ); + break; + } + case ForIterType: { + compileForIter( pd, code ); + break; + } + case ReturnType: { + /* Evaluate the exrepssion. */ + UniqueType *exprUT = expr->evaluate( pd, code ); + + UniqueType *resUT = pd->curFunction->typeRef->lookupType( pd ); + if ( !castAssignment( pd, code, resUT, 0, exprUT ) ) + error(loc) << "return value wrong type" << endp; + + code.append( IN_SAVE_RET ); + + /* The loop cleanup code. */ + if ( pd->loopCleanup != 0 ) + code.append( *pd->loopCleanup ); + + /* Jump to the return label. The distnacnce will be filled in + * later. */ + pd->returnJumps.append( code.length() ); + code.append( IN_JMP ); + code.appendHalf( 0 ); + break; + } + case BreakType: { + pd->breakJumps.append( code.length() ); + code.append( IN_JMP ); + code.appendHalf( 0 ); + break; + } + case YieldType: { + /* take a reference and yield it. Immediately reset the referece. */ + varRef->preEvaluateRef( pd, code ); + ObjField *objField = varRef->evaluateRef( pd, code, 0 ); + code.append( IN_YIELD ); + + if ( varRef->qual->length() > 0 ) { + code.append( IN_POP_N_WORDS ); + code.appendHalf( (short)(varRef->qual->length()*2) ); + } + + objField->refActive = false; + break; + } + } +} + +void CodeBlock::compile( ParseData *pd, CodeVect &code ) const +{ + for ( StmtList::Iter stmt = *stmtList; stmt.lte(); stmt++ ) + stmt->compile( pd, code ); +} + +void ParseData::addProdRedObjectVar( ObjectDef *localFrame, KlangEl *nonTerm ) +{ + UniqueType *prodNameUT = findUniqueType( TYPE_TREE, nonTerm ); + TypeRef *typeRef = new TypeRef( InputLoc(), prodNameUT ); + ObjField *el = new ObjField( InputLoc(), typeRef, "lhs" ); + + /* Is the only item pushed to the stack just before a reduction action is + * executed. We rely on a zero offset. */ + el->beenReferenced = true; + el->beenInitialized = true; + el->isLhsEl = true; + el->offset = 0; + + initLocalInstructions( el ); + + localFrame->objFieldMap->insert( el->name, el ); + localFrame->objFieldList->append( el ); +} + +void ParseData::addProdRHSVars( ObjectDef *localFrame, ProdElList *prodElList ) +{ + long position = 1; + for ( ProdElList::Iter rhsEl = *prodElList; rhsEl.lte(); rhsEl++, position++ ) { + if ( rhsEl->type == PdaFactor::ReferenceType ) { + TypeRef *typeRef = new TypeRef( rhsEl->loc, rhsEl->nspaceQual, rhsEl->refName ); + + /* Use an offset of zero. For frame objects we compute the offset on + * demand. */ + String name( 8, "r%d", position ); + ObjField *el = new ObjField( InputLoc(), typeRef, name ); + rhsEl->objField = el; + + /* Right hand side elements are constant. */ + el->isConst = true; + el->isRhsEl = true; + + /* Only ever fetch for reading since they are constant. */ + el->inGetR = IN_GET_LOCAL_R; + + localFrame->objFieldMap->insert( el->name, el ); + localFrame->objFieldList->append( el ); + } + } +} + +void ParseData::addMatchLength( ObjectDef *frame, KlangEl *lel ) +{ + /* Make the type ref. */ + TypeRef *typeRef = new TypeRef( InputLoc(), uniqueTypeInt ); + + /* Create the field and insert it into the map. */ + ObjField *el = new ObjField( InputLoc(), typeRef, "match_length" ); + el->beenReferenced = true; + el->beenInitialized = true; + el->isConst = true; + el->useOffset = false; + el->inGetR = IN_GET_MATCH_LENGTH_R; + frame->objFieldMap->insert( el->name, el ); + frame->objFieldList->append( el ); +} + +void ParseData::addMatchText( ObjectDef *frame, KlangEl *lel ) +{ + /* Make the type ref. */ + TypeRef *typeRef = new TypeRef( InputLoc(), uniqueTypeStr ); + + /* Create the field and insert it into the map. */ + ObjField *el = new ObjField( InputLoc(), typeRef, "match_text" ); + el->beenReferenced = true; + el->beenInitialized = true; + el->isConst = true; + el->useOffset = false; + el->inGetR = IN_GET_MATCH_TEXT_R; + frame->objFieldMap->insert( el->name, el ); + frame->objFieldList->append( el ); +} + +void ParseData::initFieldInstructions( ObjField *el ) +{ + el->inGetR = IN_GET_FIELD_R; + el->inGetWC = IN_GET_FIELD_WC; + el->inGetWV = IN_GET_FIELD_WV; + el->inSetWC = IN_SET_FIELD_WC; + el->inSetWV = IN_SET_FIELD_WV; +} + +void ParseData::initLocalInstructions( ObjField *el ) +{ + el->inGetR = IN_GET_LOCAL_R; + el->inGetWC = IN_GET_LOCAL_WC; + el->inSetWC = IN_SET_LOCAL_WC; +} + +void ParseData::initLocalRefInstructions( ObjField *el ) +{ + el->inGetR = IN_GET_LOCAL_REF_R; + el->inGetWC = IN_GET_LOCAL_REF_WC; + el->inSetWC = IN_SET_LOCAL_REF_WC; +} + +void ParseData::initIntObject( ) +{ + ObjFieldMap *fieldMap = new ObjFieldMap; + ObjFieldList *fieldList = new ObjFieldList; + ObjMethodMap *methodMap = new ObjMethodMap; + intObj = new ObjectDef( ObjectDef::BuiltinType, "int", + fieldMap, fieldList, methodMap, nextObjectId++ ); + intKlangEl->objectDef = intObj; + + initFunction( uniqueTypeStr, intObj, "to_string", IN_INT_TO_STR, IN_INT_TO_STR, true ); +} + +/* Add a constant length field to the object. + * Opcode supplied by the caller. */ +void ParseData::addLengthField( ObjectDef *objDef, Code getLength ) +{ + /* Create the "length" field. */ + TypeRef *typeRef = new TypeRef( InputLoc(), uniqueTypeInt ); + ObjField *el = new ObjField( InputLoc(), typeRef, "length" ); + el->beenReferenced = true; + el->beenInitialized = true; + el->isConst = true; + el->useOffset = false; + el->inGetR = getLength; + + objDef->objFieldMap->insert( el->name, el ); + objDef->objFieldList->append( el ); +} + +void ParseData::initStrObject( ) +{ + ObjFieldMap *fieldMap = new ObjFieldMap; + ObjFieldList *fieldList = new ObjFieldList; + ObjMethodMap *methodMap = new ObjMethodMap; + strObj = new ObjectDef( ObjectDef::BuiltinType, "str", + fieldMap, fieldList, methodMap, nextObjectId++ ); + strKlangEl->objectDef = strObj; + + initFunction( uniqueTypeInt, strObj, "atoi", IN_STR_ATOI, IN_STR_ATOI, true ); + initFunction( uniqueTypeInt, strObj, "uord8", IN_STR_UORD8, IN_STR_UORD8, true ); + initFunction( uniqueTypeInt, strObj, "sord8", IN_STR_SORD8, IN_STR_SORD8, true ); + initFunction( uniqueTypeInt, strObj, "uord16", IN_STR_UORD16, IN_STR_UORD16, true ); + initFunction( uniqueTypeInt, strObj, "sord16", IN_STR_SORD16, IN_STR_SORD16, true ); + initFunction( uniqueTypeInt, strObj, "uord32", IN_STR_UORD32, IN_STR_UORD32, true ); + initFunction( uniqueTypeInt, strObj, "sord32", IN_STR_SORD32, IN_STR_SORD32, true ); + addLengthField( strObj, IN_STR_LENGTH ); +} + +void ParseData::initStreamObject( ) +{ + ObjFieldMap *fieldMap = new ObjFieldMap; + ObjFieldList *fieldList = new ObjFieldList; + ObjMethodMap *methodMap = new ObjMethodMap; + streamObj = new ObjectDef( ObjectDef::BuiltinType, "stream", + fieldMap, fieldList, methodMap, nextObjectId++ ); + streamKlangEl->objectDef = streamObj; + +// initFunction( uniqueTypeInt, strObj, "atoi", IN_STR_ATOI, IN_STR_ATOI, true ); +// initFunction( uniqueTypeInt, strObj, "uord8", IN_STR_UORD8, IN_STR_UORD8, true ); +// initFunction( uniqueTypeInt, strObj, "sord8", IN_STR_SORD8, IN_STR_SORD8, true ); +// initFunction( uniqueTypeInt, strObj, "uord16", IN_STR_UORD16, IN_STR_UORD16, true ); +// initFunction( uniqueTypeInt, strObj, "sord16", IN_STR_SORD16, IN_STR_SORD16, true ); +// initFunction( uniqueTypeInt, strObj, "uord32", IN_STR_UORD32, IN_STR_UORD32, true ); +// initFunction( uniqueTypeInt, strObj, "sord32", IN_STR_SORD32, IN_STR_SORD32, true ); +// addLengthField( strObj, IN_STR_LENGTH ); +} + +ObjField *ParseData::makeDataEl() +{ + /* Create the "data" field. */ + TypeRef *typeRef = new TypeRef( InputLoc(), uniqueTypeStr ); + ObjField *el = new ObjField( InputLoc(), typeRef, "data" ); + + /* Setting beenReferenced to true prevents us from assigning instructions + * and an offset to the field. */ + + el->beenReferenced = true; + el->beenInitialized = true; + el->useOffset = false; + el->inGetR = IN_GET_TOKEN_DATA_R; + el->inSetWC = IN_SET_TOKEN_DATA_WC; + el->inSetWV = IN_SET_TOKEN_DATA_WV; + return el; +} + +ObjField *ParseData::makePosEl() +{ + /* Create the "data" field. */ + TypeRef *typeRef = new TypeRef( InputLoc(), uniqueTypeInt ); + ObjField *el = new ObjField( InputLoc(), typeRef, "pos" ); + + /* Setting beenReferenced to true prevents us from assigning instructions + * and an offset to the field. */ + + el->isConst = true; + el->beenReferenced = true; + el->beenInitialized = true; + el->useOffset = false; + el->inGetR = IN_GET_TOKEN_POS_R; + return el; +} + +void ParseData::initTokenObjects( ) +{ + /* Make a default object Definition. */ + ObjFieldMap *fieldMap = new ObjFieldMap; + ObjFieldList *fieldList = new ObjFieldList; + ObjMethodMap *methodMap = new ObjMethodMap; + tokenObj = new ObjectDef( ObjectDef::BuiltinType, "token", fieldMap, fieldList, + methodMap, nextObjectId++ ); + + ObjField *dataEl = makeDataEl(); + tokenObj->objFieldMap->insert( dataEl->name, dataEl ); + tokenObj->objFieldList->append( dataEl ); + + ObjField *posEl = makePosEl(); + tokenObj->objFieldMap->insert( posEl->name, posEl ); + tokenObj->objFieldList->append( posEl ); + + + /* Give all user terminals the token object type. */ + for ( LelList::Iter lel = langEls; lel.lte(); lel++ ) { + if ( lel->isUserTerm ) { + if ( lel->objectDef == 0 ) + lel->objectDef = tokenObj; + else { + /* Create the "data" field. */ + ObjField *dataEl = makeDataEl(); + lel->objectDef->objFieldMap->insert( dataEl->name, dataEl ); + lel->objectDef->objFieldList->append( dataEl ); + + /* Create the "pos" field. */ + ObjField *posEl = makePosEl(); + lel->objectDef->objFieldMap->insert( posEl->name, posEl ); + lel->objectDef->objFieldList->append( posEl ); + } + } + } +} + +void ParseData::findLocalTrees( CharSet &trees ) +{ + /* We exlcude "lhs" from being downrefed because we need to use if after + * the frame is is cleaned and so it must survive. */ + for ( ObjFieldMap::Iter of = *curLocalFrame->objFieldMap; of.lte(); of++ ) { + ObjField *el = of->value; + /* FIXME: This test needs to be improved. Match_text was getting + * through before useOffset was tested. What will? */ + if ( el->useOffset && !el->isLhsEl && el->beenReferenced ) { + UniqueType *ut = el->typeRef->lookupType( this ); + if ( ut->typeId == TYPE_TREE || ut->typeId == TYPE_PTR ) + trees.insert( el->offset ); + } + } +} + +void ParseData::addSaveLHS( Definition *prod, CodeVect &code, long &insertPos ) +{ + CodeBlock *block = prod->redBlock; + + /* If the lhs tree is dirty then we will need to save off the old lhs + * before it gets modified. We want to avoid this for attribute + * modifications. The computation of dirtyTree should deal with this for + * us. */ + ObjFieldMapEl *lhsFieldMapEl = block->localFrame->objFieldMap->find("lhs"); + assert( lhsFieldMapEl != 0 ); + ObjField *lhsField = lhsFieldMapEl->value; + + if ( lhsField->dirtyTree ) + code.insert( insertPos, IN_SAVE_LHS ); +} + +void ParseData::addProdRHSLoads( Definition *prod, CodeVect &code, long &insertPos ) +{ + CodeVect loads; + long elPos = 0; + for ( ProdElList::Iter rhsEl = *prod->prodElList; rhsEl.lte(); rhsEl++, elPos++ ) { + if ( rhsEl->type == PdaFactor::ReferenceType ) { + if ( rhsEl->objField->beenReferenced ) { + loads.append ( IN_INIT_RHS_EL ); + loads.appendHalf( elPos ); + loads.appendHalf( rhsEl->objField->offset ); + } + } + } + + /* Insert and update the insert position. */ + code.insert( insertPos, loads ); + insertPos += loads.length(); +} + + +void ParseData::compileReductionCode( Definition *prod ) +{ + CodeBlock *block = prod->redBlock; + + /* Init the compilation context. */ + compileContext = CompileReduction; + curLocalFrame = block->localFrame; + revertOn = true; + block->frameId = nextFrameId++; + + CodeVect &code = block->codeWV; + + /* Add the alloc frame opcode. We don't have the right + * frame size yet. We will fill it in later. */ + code.append( IN_INIT_LOCALS ); + code.appendHalf( 0 ); + long afterInit = code.length(); + + /* Compile the reduce block. */ + block->compile( this, code ); + + /* We have the frame size now. Set in the alloc frame instruction. */ + long frameSize = curLocalFrame->size(); + code.setHalf( 1, frameSize ); + + /* Might need to preserve the LHS for backtracking. The afterInit + * var may be updated by this call. */ + addSaveLHS( prod, code, afterInit ); + + /* Might need to load right hand side values. */ + addProdRHSLoads( prod, code, afterInit ); + + code.append( IN_POP_LOCALS ); + code.appendHalf( block->frameId ); + code.appendHalf( frameSize ); + + code.append( IN_STOP ); + + /* Now that compilation is done variables are referenced. Make the local + * trees descriptor. */ + findLocalTrees( block->trees ); +} + +void ParseData::compileTranslateBlock( KlangEl *langEl ) +{ + CodeBlock *block = langEl->transBlock; + + /* Set up compilation context. */ + compileContext = CompileTranslation; + curLocalFrame = block->localFrame; + revertOn = true; + block->frameId = nextFrameId++; + + /* References to the reduce item. */ + addMatchLength( curLocalFrame, langEl ); + addMatchText( curLocalFrame, langEl ); + initFunction( uniqueTypeStr, curLocalFrame, "pull", + IN_STREAM_PULL, IN_STREAM_PULL, uniqueTypeStream, uniqueTypeInt, true ); + initFunction( uniqueTypeInt, curLocalFrame, "push", + IN_STREAM_PUSH, IN_STREAM_PUSH, uniqueTypeStream, uniqueTypeAny, true ); + + initFunction( uniqueTypeInt, curLocalFrame, "send", + IN_SEND, IN_SEND, uniqueTypeAny, true ); + initFunction( uniqueTypeInt, curLocalFrame, "send_ignore", + IN_IGNORE, IN_IGNORE, uniqueTypeAny, true ); + + CodeVect &code = block->codeWV; + + /* Add the alloc frame opcode. We don't have the right + * frame size yet. We will fill it in later. */ + code.append( IN_INIT_LOCALS ); + code.appendHalf( 0 ); + + if ( langEl->tokenDef->reCaptureVect.length() > 0 ) { + code.append( IN_INIT_CAPTURES ); + code.append( langEl->tokenDef->reCaptureVect.length() ); + + ObjFieldList::Iter f = *curLocalFrame->objFieldList; + for ( int i = 0; i < langEl->tokenDef->reCaptureVect.length(); i++, f++ ) + curLocalFrame->referenceField( this, f->value ); + } + + /* Set the local frame and compile the reduce block. */ + block->compile( this, code ); + + /* We have the frame size now. Set in the alloc frame instruction. */ + long frameSize = curLocalFrame->size(); + code.setHalf( 1, frameSize ); + + code.append( IN_POP_LOCALS ); + code.appendHalf( block->frameId ); + code.appendHalf( frameSize ); + + code.append( IN_STOP ); + + /* Now that compilation is done variables are referenced. Make the local + * trees descriptor. */ + findLocalTrees( block->trees ); +} + +void ParseData::compilePreEof( TokenRegion *region ) +{ + CodeBlock *block = region->preEofBlock; + + /* Set up compilation context. */ + compileContext = CompileTranslation; + curLocalFrame = region->preEofBlock->localFrame; + revertOn = true; + block->frameId = nextFrameId++; + + /* References to the reduce item. */ +// addMatchLength( curLocalFrame, langEl ); +// addMatchText( curLocalFrame, langEl ); +// initFunction( uniqueTypeStr, curLocalFrame, "pull", +// IN_STREAM_PULL, IN_STREAM_PULL, uniqueTypeStream, uniqueTypeInt, true ); + + initFunction( uniqueTypeInt, curLocalFrame, "send", + IN_SEND, IN_SEND, uniqueTypeAny, true ); + initFunction( uniqueTypeInt, curLocalFrame, "send_ignore", + IN_IGNORE, IN_IGNORE, uniqueTypeAny, true ); + + CodeVect &code = block->codeWV; + + /* Add the alloc frame opcode. We don't have the right + * frame size yet. We will fill it in later. */ + code.append( IN_INIT_LOCALS ); + code.appendHalf( 0 ); + + /* Set the local frame and compile the reduce block. */ + block->compile( this, code ); + + /* We have the frame size now. Set in the alloc frame instruction. */ + long frameSize = curLocalFrame->size(); + code.setHalf( 1, frameSize ); + + code.append( IN_POP_LOCALS ); + code.appendHalf( block->frameId ); + code.appendHalf( frameSize ); + + code.append( IN_STOP ); + + /* Now that compilation is done variables are referenced. Make the local + * trees descriptor. */ + findLocalTrees( block->trees ); +} + +void ParseData::compileRootBlock( ) +{ + CodeBlock *block = rootCodeBlock; + + /* The root block never needs to be reverted. */ + + /* Set up the compile context. No locals are needed for the root code + * block, but we need an empty local frame for the compile. */ + compileContext = CompileRoot; + curLocalFrame = rootLocalFrame; + revertOn = false; + + /* The block needs a frame id. */ + block->frameId = nextFrameId++; + + /* The root block is not reverted. */ + CodeVect &code = block->codeWC; + + /* Add the alloc frame opcode. We don't have the right + * frame size yet. We will fill it in later. */ + code.append( IN_INIT_LOCALS ); + code.appendHalf( 0 ); + + block->compile( this, code ); + + /* We have the frame size now. Store it in frame init. */ + long frameSize = curLocalFrame->size(); + code.setHalf( 1, frameSize ); + + code.append( IN_POP_LOCALS ); + code.appendHalf( block->frameId ); + code.appendHalf( frameSize ); + + code.append( IN_STOP ); + + /* Make the local trees descriptor. */ + findLocalTrees( block->trees ); +} + +void ParseData::initAllLanguageObjects() +{ + /* Init all user object fields (need consistent size). */ + for ( LelList::Iter lel = langEls; lel.lte(); lel++ ) { + ObjectDef *obj = lel->objectDef; + if ( obj != 0 ) { + /* Init all fields of the object. */ + for ( ObjFieldList::Iter f = *obj->objFieldList; f.lte(); f++ ) + obj->initField( this, f->value ); + } + } + + /* Init all fields of the global object. */ + for ( ObjFieldList::Iter f = *globalObjectDef->objFieldList; f.lte(); f++ ) + globalObjectDef->initField( this, f->value ); +} + +void ParseData::initMapFunctions( GenericType *gen ) +{ + addLengthField( gen->objDef, IN_MAP_LENGTH ); + initFunction( gen->utArg, gen->objDef, "find", + IN_MAP_FIND, IN_MAP_FIND, gen->keyUT, true ); + initFunction( uniqueTypeInt, gen->objDef, "insert", + IN_MAP_INSERT_WV, IN_MAP_INSERT_WC, gen->keyUT, gen->utArg, false ); + initFunction( uniqueTypeInt, gen->objDef, "store", + IN_MAP_STORE_WV, IN_MAP_STORE_WC, gen->keyUT, gen->utArg, false ); + initFunction( gen->utArg, gen->objDef, "remove", + IN_MAP_REMOVE_WV, IN_MAP_REMOVE_WC, gen->keyUT, false ); +} + +void ParseData::initListFunctions( GenericType *gen ) +{ + addLengthField( gen->objDef, IN_LIST_LENGTH ); + + initFunction( uniqueTypeInt, gen->objDef, "append", + IN_LIST_APPEND_WV, IN_LIST_APPEND_WC, gen->utArg, false ); + initFunction( uniqueTypeInt, gen->objDef, "push", + IN_LIST_APPEND_WV, IN_LIST_APPEND_WC, gen->utArg, false ); + + initFunction( gen->utArg, gen->objDef, "remove_end", + IN_LIST_REMOVE_END_WV, IN_LIST_REMOVE_END_WC, false ); + initFunction( gen->utArg, gen->objDef, "pop", + IN_LIST_REMOVE_END_WV, IN_LIST_REMOVE_END_WC, false ); +} + +void ParseData::initListField( GenericType *gen, const char *name, int offset ) +{ + /* Make the type ref and create the field. */ + TypeRef *typeRef = new TypeRef( InputLoc(), gen->utArg ); + ObjField *el = new ObjField( InputLoc(), typeRef, name ); + + el->inGetR = IN_GET_LIST_MEM_R; + el->inGetWC = IN_GET_LIST_MEM_WC; + el->inGetWV = IN_GET_LIST_MEM_WV; + el->inSetWC = IN_SET_LIST_MEM_WC; + el->inSetWV = IN_SET_LIST_MEM_WV; + + gen->objDef->objFieldMap->insert( el->name, el ); + gen->objDef->objFieldList->append( el ); + + el->useOffset = true; + el->beenReferenced = true; + el->beenInitialized = true; + + /* Zero for head, One for tail. */ + el->offset = offset; +} + +void ParseData::initListFields( GenericType *gen ) +{ + initListField( gen, "head", 0 ); + initListField( gen, "tail", 1 ); + initListField( gen, "top", 1 ); +} + +void ParseData::initVectorFunctions( GenericType *gen ) +{ + addLengthField( gen->objDef, IN_VECTOR_LENGTH ); + initFunction( uniqueTypeInt, gen->objDef, "append", + IN_VECTOR_APPEND_WV, IN_VECTOR_APPEND_WC, gen->utArg, false ); + initFunction( uniqueTypeInt, gen->objDef, "insert", + IN_VECTOR_INSERT_WV, IN_VECTOR_INSERT_WC, uniqueTypeInt, gen->utArg, false ); +} + +void ParseData::resolveGenericTypes() +{ + for ( NamespaceList::Iter ns = namespaceList; ns.lte(); ns++ ) { + for ( GenericList::Iter gen = ns->genericList; gen.lte(); gen++ ) { + gen->utArg = gen->typeArg->lookupType( this ); + + if ( gen->typeId == GEN_MAP ) + gen->keyUT = gen->keyTypeArg->lookupType( this ); + + ObjFieldMap *fieldMap = new ObjFieldMap; + ObjFieldList *fieldList = new ObjFieldList; + ObjMethodMap *methodMap = new ObjMethodMap; + gen->objDef = new ObjectDef( ObjectDef::BuiltinType, + gen->name, fieldMap, fieldList, methodMap, nextObjectId++ ); + + switch ( gen->typeId ) { + case GEN_MAP: + initMapFunctions( gen ); + break; + case GEN_LIST: + initListFunctions( gen ); + initListFields( gen ); + break; + case GEN_VECTOR: + initVectorFunctions( gen ); + break; + } + + gen->langEl->objectDef = gen->objDef; + } + } +} + +void ParseData::makeFuncVisible( Function *func, bool isUserIter ) +{ + /* Need an object for the local frame. */ + curLocalFrame = func->codeBlock->localFrame; + func->localFrame = func->codeBlock->localFrame; + + /* Set up the parameters. */ + long paramPos = 0, paramListSize = 0; + UniqueType **paramUTs = new UniqueType*[func->paramList->length()]; + for ( ParameterList::Iter param = *func->paramList; param.lte(); param++ ) { + paramUTs[paramPos] = param->typeRef->lookupType( this ); + + if ( func->localFrame->objFieldMap->find( param->name ) != 0 ) + error(param->loc) << "parameter " << param->name << " redeclared" << endp; + + func->localFrame->objFieldMap->insert( param->name, param ); + func->localFrame->objFieldList->append( param ); + param->beenInitialized = true; + param->pos = paramPos; + + /* Initialize the object field as a local variable. We also want trees + * downreffed. */ + if ( paramUTs[paramPos]->typeId == TYPE_REF ) + initLocalRefInstructions( param ); + else + initLocalInstructions( param ); + + paramListSize += sizeOfField( paramUTs[paramPos] ); + paramPos += 1; + } + + /* Param offset is relative to one past the last item in the array of + * words containing the args. */ + long paramOffset = 0; + for ( ParameterList::Iter param = *func->paramList; param.lte(); param++ ) { + /* Moving downward, and need the offset to point to the lower half of + * the argument. */ + paramOffset -= sizeOfField( paramUTs[param->pos] ); + + /* How much space do we need to make for call overhead. */ + long frameAfterArgs = isUserIter ? IFR_AA : FR_AA; + + /* Going up first we have the frame data, then maybe + * the user iterator, then the args from high to low. */ + param->offset = frameAfterArgs + + ( isUserIter ? ( sizeof(UserIter) / sizeof(Word) ) : 0 ) + + paramListSize + paramOffset; + } + + func->paramListSize = paramListSize; + func->paramUTs = paramUTs; + + /* Insert the function into the global function map. */ + UniqueType *returnUT = func->typeRef != 0 ? + func->typeRef->lookupType(this) : uniqueTypeInt; + ObjMethod *objMethod = new ObjMethod( returnUT, func->name, + IN_CALL_WV, IN_CALL_WC, + func->paramList->length(), paramUTs, func->paramList, false ); + objMethod->funcId = func->funcId; + objMethod->useFuncId = true; + objMethod->useCallObj = false; + objMethod->func = func; + + if ( isUserIter ) { + IterDef *uiter = findIterDef( IterDef::User, func ); + objMethod->iterDef = uiter; + } + + globalObjectDef->objMethodMap->insert( func->name, objMethod ); +} + +void ParseData::compileUserIter( Function *func, CodeVect &code ) +{ + CodeBlock *block = func->codeBlock; + + /* Add the alloc frame opcode. We don't have the right + * frame size yet. We will fill it in later. */ + code.append( IN_INIT_LOCALS ); + code.appendHalf( 0 ); + + /* Compile the block. */ + block->compile( this, code ); + + /* We have the frame size now. Set in the alloc frame instruction. */ + int frameSize = func->localFrame->size(); + code.setHalf( 1, frameSize ); + + /* Check for a return statement. */ + if ( block->stmtList->length() == 0 || + block->stmtList->tail->type != LangStmt::YieldType ) + { + /* Push the return value. */ + code.append( IN_LOAD_NIL ); + code.append( IN_YIELD ); + } +} + +void ParseData::compileUserIter( Function *func ) +{ + CodeBlock *block = func->codeBlock; + + /* Set up the context. */ + compileContext = CompileFunction; + curFunction = func; + block->frameId = nextFrameId++; + + /* Process the params, etc. */ + makeFuncVisible( func, true ); + + /* Compile for revert and commit. */ + revertOn = true; + compileUserIter( func, block->codeWV ); + + revertOn = false; + compileUserIter( func, block->codeWC ); + + /* Now that compilation is done variables are referenced. Make the local + * trees descriptor. */ + findLocalTrees( block->trees ); + + /* FIXME: Need to deal with the freeing of local trees. */ +} + +/* Called for each type of function compile: revert and commit. */ +void ParseData::compileFunction( Function *func, CodeVect &code ) +{ + CodeBlock *block = func->codeBlock; + + /* Add the alloc frame opcode. We don't have the right + * frame size yet. We will fill it in later. */ + code.append( IN_INIT_LOCALS ); + code.appendHalf( 0 ); + + /* Compile the block. */ + block->compile( this, code ); + + /* We have the frame size now. Set in the alloc frame instruction. */ + int frameSize = func->localFrame->size(); + code.setHalf( 1, frameSize ); + + /* Check for a return statement. */ + if ( block->stmtList->length() == 0 || + block->stmtList->tail->type != LangStmt::ReturnType ) + { + /* Push the return value. */ + code.append( IN_LOAD_NIL ); + code.append( IN_SAVE_RET ); + } + + /* Compute the jump distance for the return jumps. */ + for ( LongVect::Iter rj = returnJumps; rj.lte(); rj++ ) { + long distance = code.length() - *rj - 3; + code.setHalf( *rj+1, distance ); + } + + /* Reset the vector of return jumps. */ + returnJumps.empty(); + + /* Return cleans up the stack (including the args) and leaves the return + * value on the top. */ + code.append( IN_RET ); + code.appendHalf( func->funcId ); +} + +void ParseData::compileFunction( Function *func ) +{ + CodeBlock *block = func->codeBlock; + + /* Set up the compilation context. */ + compileContext = CompileFunction; + curFunction = func; + + /* Assign a frame Id. */ + block->frameId = nextFrameId++; + + makeFuncVisible( func, false ); + + /* Compile once for revert. */ + revertOn = true; + compileFunction( func, block->codeWV ); + + /* Compile once for commit. */ + revertOn = false; + compileFunction( func, block->codeWC ); + + /* Now that compilation is done variables are referenced. Make the local + * trees descriptor. */ + findLocalTrees( block->trees ); +} + +void ParseData::makeDefaultIterators() +{ + /* Tree iterator. */ + { + UniqueType *anyRefUT = findUniqueType( TYPE_REF, anyKlangEl ); + ObjMethod *objMethod = initFunction( uniqueTypeAny, globalObjectDef, + "triter", IN_HALT, IN_HALT, anyRefUT, true ); + + IterDef *triter = findIterDef( IterDef::Tree ); + objMethod->iterDef = triter; + } + + /* Child iterator. */ + { + UniqueType *anyRefUT = findUniqueType( TYPE_REF, anyKlangEl ); + ObjMethod *objMethod = initFunction( uniqueTypeAny, globalObjectDef, + "child", IN_HALT, IN_HALT, anyRefUT, true ); + + IterDef *triter = findIterDef( IterDef::Child ); + objMethod->iterDef = triter; + } + + /* Reverse iterator. */ + { + UniqueType *anyRefUT = findUniqueType( TYPE_REF, anyKlangEl ); + ObjMethod *objMethod = initFunction( uniqueTypeAny, globalObjectDef, + "rev_child", IN_HALT, IN_HALT, anyRefUT, true ); + + IterDef *triter = findIterDef( IterDef::RevChild ); + objMethod->iterDef = triter; + } +} + +void ParseData::addStdin() +{ + /* Make the type ref. */ + TypeRef *typeRef = new TypeRef( InputLoc(), uniqueTypeStream ); + + /* Create the field and insert it into the map. */ + ObjField *el = new ObjField( InputLoc(), typeRef, "stdin" ); + el->beenReferenced = true; + el->beenInitialized = true; + el->isConst = true; + el->useOffset = false; + el->inGetR = IN_GET_STDIN; + globalObjectDef->objFieldMap->insert( el->name, el ); + globalObjectDef->objFieldList->append( el ); +} + +void ParseData::addStdout() +{ + /* Make the type ref. */ + TypeRef *typeRef = new TypeRef( InputLoc(), uniqueTypeStr ); + + /* Create the field and insert it into the map. */ + ObjField *el = new ObjField( InputLoc(), typeRef, "stout" ); + el->beenReferenced = true; + el->beenInitialized = true; + el->isConst = true; + el->useOffset = false; + el->inGetR = IN_GET_STDOUT; + globalObjectDef->objFieldMap->insert( el->name, el ); + globalObjectDef->objFieldList->append( el ); +} + +void ParseData::addStderr() +{ + /* Make the type ref. */ + TypeRef *typeRef = new TypeRef( InputLoc(), uniqueTypeStr ); + + /* Create the field and insert it into the map. */ + ObjField *el = new ObjField( InputLoc(), typeRef, "stderr" ); + el->beenReferenced = true; + el->beenInitialized = true; + el->isConst = true; + el->useOffset = false; + el->inGetR = IN_GET_STDERR; + globalObjectDef->objFieldMap->insert( el->name, el ); + globalObjectDef->objFieldList->append( el ); +} + +void ParseData::initGlobalFunctions() +{ + ObjMethod *method; + + method = initFunction( uniqueTypeStream, globalObjectDef, "open_file", + IN_OPEN_FILE, IN_OPEN_FILE, uniqueTypeStr, true ); + method->useCallObj = false; + + addStdin(); + addStdout(); + addStderr(); +} + +void ParseData::compileByteCode() +{ + initUniqueTypes(); + initIntObject(); + initStrObject(); + initStreamObject(); + initTokenObjects(); + makeDefaultIterators(); + initAllLanguageObjects(); + resolveGenericTypes(); + + initGlobalFunctions(); + + /* The function info structure relies on functions being compile first, + * then iterators. */ + + /* Compile functions. */ + for ( FunctionList::Iter f = functionList; f.lte(); f++ ) { + if ( f->isUserIter ) + compileUserIter( f ); + else + compileFunction( f ); + } + + /* Compile the reduction code. */ + for ( DefList::Iter prod = prodList; prod.lte(); prod++ ) { + if ( prod->redBlock != 0 ) + compileReductionCode( prod ); + } + + /* Compile the token translation code. */ + for ( LelList::Iter lel = langEls; lel.lte(); lel++ ) { + if ( lel->transBlock != 0 ) + compileTranslateBlock( lel ); + } + + /* Compile preeof blocks. */ + for ( RegionList::Iter r = regionList; r.lte(); r++ ) { + if ( r->preEofBlock != 0 ) + compilePreEof( r ); + } + + /* Compile the init code */ + compileRootBlock( ); +} |