diff options
Diffstat (limited to 'colm/synthesis.cc')
-rw-r--r-- | colm/synthesis.cc | 3441 |
1 files changed, 3441 insertions, 0 deletions
diff --git a/colm/synthesis.cc b/colm/synthesis.cc new file mode 100644 index 00000000..ef91d2f3 --- /dev/null +++ b/colm/synthesis.cc @@ -0,0 +1,3441 @@ +/* + * 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 "bytecode.h" +#include "parsedata.h" +#include "fsmrun.h" +#include "pdarun.h" +#include "input.h" +#include <iostream> +#include <assert.h> + +using std::cout; +using std::cerr; +using std::endl; + +void Compiler::initUniqueTypes( ) +{ + uniqueTypeNil = new UniqueType( TYPE_NIL ); + uniqueTypePtr = new UniqueType( TYPE_TREE, ptrLangEl ); + uniqueTypeBool = new UniqueType( TYPE_TREE, boolLangEl ); + uniqueTypeInt = new UniqueType( TYPE_TREE, intLangEl ); + uniqueTypeStr = new UniqueType( TYPE_TREE, strLangEl ); + uniqueTypeStream = new UniqueType( TYPE_TREE, streamLangEl ); + uniqueTypeInput = new UniqueType( TYPE_TREE, inputLangEl ); + uniqueTypeIgnore = new UniqueType( TYPE_TREE, ignoreLangEl ); + uniqueTypeAny = new UniqueType( TYPE_TREE, anyLangEl ); + + uniqeTypeMap.insert( uniqueTypeNil ); + uniqeTypeMap.insert( uniqueTypePtr ); + uniqeTypeMap.insert( uniqueTypeBool ); + uniqeTypeMap.insert( uniqueTypeInt ); + uniqeTypeMap.insert( uniqueTypeStr ); + uniqeTypeMap.insert( uniqueTypeStream ); + uniqeTypeMap.insert( uniqueTypeInput ); + uniqeTypeMap.insert( uniqueTypeIgnore ); + uniqeTypeMap.insert( uniqueTypeAny ); +} + +IterDef::IterDef( Type type ) : + type(type), + func(0), + useFuncId(false), + useSearchUT(false) +{ + switch ( type ) { + case 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; + break; + case 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; + break; + case RevChild: + inCreateWV = IN_REV_TRITER_FROM_REF; + inCreateWC = IN_REV_TRITER_FROM_REF; + inDestroy = IN_REV_TRITER_DESTROY; + inAdvance = IN_REV_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; + break; + + case Repeat: + inCreateWV = IN_TRITER_FROM_REF; + inCreateWC = IN_TRITER_FROM_REF; + inDestroy = IN_TRITER_DESTROY; + inAdvance = IN_TRITER_NEXT_REPEAT; + + 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; + break; + + case RevRepeat: + inCreateWV = IN_TRITER_FROM_REF; + inCreateWC = IN_TRITER_FROM_REF; + inDestroy = IN_TRITER_DESTROY; + inAdvance = IN_TRITER_PREV_REPEAT; + + 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; + break; + + case User: + 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 *Compiler::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 *Compiler::findIterDef( IterDef::Type type ) +{ + IterDefSetEl *el = iterDefSet.find( IterDef( type ) ); + if ( el == 0 ) + el = iterDefSet.insert( IterDef( type ) ); + return &el->key; +} + +UniqueType *Compiler::findUniqueType( int typeId ) +{ + UniqueType searchKey( typeId ); + UniqueType *uniqueType = uniqeTypeMap.find( &searchKey ); + if ( uniqueType == 0 ) { + uniqueType = new UniqueType( typeId ); + uniqeTypeMap.insert( uniqueType ); + } + return uniqueType; +} + +UniqueType *Compiler::findUniqueType( int typeId, LangEl *langEl ) +{ + UniqueType searchKey( typeId, langEl ); + UniqueType *uniqueType = uniqeTypeMap.find( &searchKey ); + if ( uniqueType == 0 ) { + uniqueType = new UniqueType( typeId, langEl ); + uniqeTypeMap.insert( uniqueType ); + } + return uniqueType; +} + +UniqueType *Compiler::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; +} + +void ObjectDef::iterPushScope() +{ + //cout << "iter push scope "; + if ( scope->childIter == 0 ) { + scope->childIter = scope->children.head; + } + else { + scope->childIter = scope->childIter->next; + /* Resetting. */ + if ( scope->childIter == 0 ) + scope ->childIter = scope->children.head; + } + + scope = scope->childIter; +} + +void ObjectDef::iterPopScope() +{ + //cout << "iter pop scope" << endl; + scope = scope->parentScope; +} + +void ObjectDef::pushScope() +{ + ObjNameScope *newScope = new ObjNameScope; + newScope->objFieldMap = new ObjFieldMap; + + newScope->parentScope = scope; + scope->children.append( newScope ); + + scope = newScope; +} + +void ObjectDef::popScope() +{ + scope = scope->parentScope; +} + +void ObjectDef::insertField( const String &name, ObjField *value ) +{ + scope->objFieldMap->insert( name, value ); + objFieldList->append( value ); +} + +/* Recurisve find through a single object def's scope. */ +ObjField *ObjectDef::findFieldInScope( const String &name, ObjNameScope *inScope ) +{ + ObjFieldMapEl *objDefMapEl = inScope->objFieldMap->find( name ); + if ( objDefMapEl != 0 ) + return objDefMapEl->value; + if ( inScope->parentScope != 0 ) + return findFieldInScope( name, inScope->parentScope ); + return 0; +} + +ObjField *ObjectDef::checkRedecl( const String &name ) +{ + //cout << "looking for " << name << endl; + ObjFieldMapEl *objDefMapEl = scope->objFieldMap->find( name ); + if ( objDefMapEl != 0 ) + return objDefMapEl->value; + return 0; + +} + +/* 0-based. */ +ObjField *ObjectDef::findFieldNum( long offset ) +{ + int fn = 0; + ObjFieldList::Iter field = *objFieldList; + while ( fn < offset ) { + fn++; + field++; + } + return field->value; +} + +ObjField *ObjectDef::findField( const String &name ) +{ + //cout << "looking for " << name << endl; + ObjField *objField = findFieldInScope( name, scope ); + if ( objField != 0 ) + return objField; + return 0; +} + +ObjMethod *ObjectDef::findMethod( const 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. */ + switch ( fieldUT->iterDef->type ) { + case IterDef::Tree: + case IterDef::Child: + case IterDef::Repeat: + case IterDef::RevRepeat: + size = sizeof(TreeIter) / sizeof(Word); + break; + case IterDef::RevChild: + size = sizeof(RevTreeIter) / sizeof(Word); + break; + + case 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; + break; + } + } + else if ( fieldUT->typeId == TYPE_REF ) + size = 2; + else + size = 1; + + return size; +} + +void ObjectDef::referenceField( Compiler *pd, ObjField *field ) +{ + field->beenReferenced = true; + initField( pd, field ); +} + +void ObjectDef::initField( Compiler *pd, ObjField *field ) +{ + if ( !field->beenInitialized ) { + field->beenInitialized = true; + UniqueType *fieldUT = field->typeRef->uniqueType; + + if ( type == FrameType ) { + nextOffset += sizeOfField( fieldUT ); + field->offset = -nextOffset; + + pd->initLocalInstructions( field ); + } + else if ( field->isRhsGet ) { + field->useOffset = false; + field->inGetR = IN_GET_RHS_VAL_R; + field->inGetWC = IN_GET_RHS_VAL_WC; + field->inGetWV = IN_GET_RHS_VAL_WV; + field->inSetWC = IN_SET_RHS_VAL_WC; + field->inSetWV = IN_SET_RHS_VAL_WC; + } + else { + field->offset = nextOffset; + nextOffset += sizeOfField( fieldUT ); + + /* Initialize the instructions. */ + pd->initFieldInstructions( field ); + } + } +} + +UniqueType *LangVarRef::loadFieldInstr( Compiler *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->uniqueType; + + /* 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 ); + } + else if ( el->isRhsGet ) { + /* Need to place the array computing the val. */ + code.append( el->rhsVal.length() ); + for ( Vector<RhsVal>::Iter rg = el->rhsVal; rg.lte(); rg++ ) { + code.append( rg->prodNum ); + code.append( rg->childNum ); + } + } + + /* If we are dealing with an iterator then dereference it. */ + if ( elUT->typeId == TYPE_ITER ) + elUT = el->typeRef->searchUniqueType; + + return elUT; +} + +ObjectDef *objDefFromUT( Compiler *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( Compiler *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. */ + ObjField *el = searchObjDef->findField( qi->data ); + if ( el == 0 ) + error(qi->loc) << "cannot resolve qualification " << qi->data << endp; + + 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->type == TypeRef::Ref ) { + 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->uniqueType; + if ( elUT->typeId == TYPE_ITER ) + elUT = el->typeRef->searchUniqueType; + + assert( qi->type == QualItem::Dot ); + + searchObjDef = objDefFromUT( pd, elUT ); + count += 1; + } + return count; +} + +void LangVarRef::loadQualification( Compiler *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. */ + ObjField *el = searchObjDef->findField( qi->data ); + if ( el == 0 ) + error(qi->loc) << "cannot resolve qualification " << qi->data << endp; + + 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::loadContextObj( Compiler *pd, CodeVect &code, + int lastPtrInQual, bool forWriting ) const +{ + /* Start the search in the global object. */ + ObjectDef *rootObj = pd->context->contextObjDef; + + 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_CONTEXT_WV ); + else + code.append( IN_LOAD_CONTEXT_WC ); + } + else { + /* Either we are reading or we are loading a pointer that will be + * dereferenced. */ + code.append( IN_LOAD_CONTEXT_R ); + } + + loadQualification( pd, code, rootObj, lastPtrInQual, forWriting, true ); +} + +void LangVarRef::loadGlobalObj( Compiler *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::loadCustom( Compiler *pd, CodeVect &code, + int lastPtrInQual, bool forWriting ) const +{ + /* Start the search in the local frame. */ + loadQualification( pd, code, pd->curLocalFrame, lastPtrInQual, forWriting, true ); +} + +void LangVarRef::loadLocalObj( Compiler *pd, CodeVect &code, + int lastPtrInQual, bool forWriting ) const +{ + /* Start the search in the local frame. */ + loadQualification( pd, code, pd->curLocalFrame, lastPtrInQual, forWriting, false ); +} + +bool LangVarRef::isLocalRef( Compiler *pd ) const +{ + if ( qual->length() > 0 ) { + if ( pd->curLocalFrame->findField( qual->data[0].data ) != 0 ) + return true; + } + else if ( pd->curLocalFrame->findField( name ) != 0 ) + return true; + else if ( pd->curLocalFrame->findMethod( name ) != 0 ) + return true; + + return false; +} + +bool LangVarRef::isContextRef( Compiler *pd ) const +{ + if ( pd->context != 0 ) { + if ( qual->length() > 0 ) { + if ( pd->context->contextObjDef->findField( qual->data[0].data ) != 0 ) + return true; + } + else if ( pd->context->contextObjDef->findField( name ) != 0 ) + return true; + else if ( pd->context->contextObjDef->findMethod( name ) != 0 ) + return true; + } + + return false; +} + +bool LangVarRef::isCustom( Compiler *pd ) const +{ + if ( qual->length() > 0 ) { + ObjField *field = pd->curLocalFrame->findField( qual->data[0].data ); + if ( field != 0 && field->isCustom ) + return true; + } + else { + ObjField *field = pd->curLocalFrame->findField( name ); + if ( field != 0 ) { + if ( field->isCustom ) + return true; + } + else { + ObjMethod *method = pd->curLocalFrame->findMethod( name ); + if ( method != 0 && method->isCustom ) + return true; + } + + } + return false; +} + +void LangVarRef::loadObj( Compiler *pd, CodeVect &code, + int lastPtrInQual, bool forWriting ) const +{ + if ( isCustom( pd ) ) + loadCustom( pd, code, lastPtrInQual, forWriting ); + else if ( isLocalRef( pd ) ) + loadLocalObj( pd, code, lastPtrInQual, forWriting ); + else if ( isContextRef( pd ) ) + loadContextObj( pd, code, lastPtrInQual, forWriting ); + else + loadGlobalObj( pd, code, lastPtrInQual, forWriting ); +} + +VarRefLookup LangVarRef::lookupQualification( Compiler *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. */ + ObjField *el = searchObjDef->findField( qi->data ); + if ( el == 0 ) + error(qi->loc) << "cannot resolve qualification " << qi->data << endp; + + /* Lookup the type of the field. */ + UniqueType *qualUT = el->typeRef->uniqueType; + + /* If we are dealing with an iterator then dereference it. */ + if ( qualUT->typeId == TYPE_ITER ) + qualUT = el->typeRef->searchUniqueType; + + /* 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->searchUniqueType; + 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( Compiler *pd ) const +{ + ObjectDef *rootDef; + if ( isLocalRef( pd ) ) + rootDef = pd->curLocalFrame; + else if ( isContextRef( pd ) ) + rootDef = pd->context->contextObjDef; + else + rootDef = pd->globalObjectDef; + + return lookupQualification( pd, rootDef ); +} + +VarRefLookup LangVarRef::lookupField( Compiler *pd ) const +{ + /* Lookup the object that the field is in. */ + VarRefLookup lookup = lookupObj( pd ); + + /* Lookup the field. */ + ObjField *field = lookup.inObject->findField( name ); + if ( field == 0 ) + error(loc) << "cannot find name " << name << " in object" << endp; + + lookup.objField = field; + lookup.uniqueType = field->typeRef->uniqueType; + + if ( field->typeRef->searchUniqueType != 0 ) + lookup.iterSearchUT = field->typeRef->searchUniqueType; + + return lookup; +} + + +VarRefLookup LangVarRef::lookupMethod( Compiler *pd ) +{ + /* 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 ) { + /* Not found as a method, try it as an object on which we will call a + * default function. */ + qual->append( QualItem( InputLoc(), name, QualItem::Dot ) ); + name = "finish"; + + /* Lookup the object that the field is in. */ + VarRefLookup lookup = lookupObj( pd ); + + /* Find the method. */ + assert( lookup.inObject->objMethodMap != 0 ); + 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( Compiler *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( Compiler *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->anyLangEl && + 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 a tree from a reference. */ + if ( destUT->typeId == TYPE_TREE && srcUT->typeId == TYPE_REF && + 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( Compiler *pd, CodeVect &code, + ObjectDef *inObject, UniqueType *exprUT, bool revert ) const +{ + ObjField *el = inObject->findField( name ); + if ( el == 0 ) + error(loc) << "cannot find name " << name << " in object" << endp; + + setFieldInstr( pd, code, inObject, el, exprUT, revert ); +} + +void LangVarRef::setFieldIter( Compiler *pd, CodeVect &code, + ObjectDef *inObject, UniqueType *objUT, UniqueType *exprType, bool revert ) const +{ + ObjField *el = inObject->findField( name ); + if ( el == 0 ) + error(loc) << "cannot find name " << name << " in object" << endp; + + code.append( objUT->iterDef->inSetCurWC ); + code.appendHalf( el->offset ); +} + +UniqueType *LangVarRef::evaluate( Compiler *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( Compiler *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( Compiler *pd, CodeVect &code ) const +{ + VarRefLookup lookup = lookupField( pd ); + + canTakeRef( pd, lookup ); + + loadQualificationRefs( pd, code ); + + return lookup.objField; +} + +/* Return the field referenced. */ +ObjField *LangVarRef::evaluateRef( Compiler *pd, CodeVect &code, long pushCount ) const +{ + 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->type == TypeRef::Ref ) { + 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( Compiler *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( Compiler *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( Compiler *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 ) { + if ( lookup.objMethod->opcodeWV == IN_PARSE_FINISH_WV ) { + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FINISH_WV ); + code.appendHalf( 0 ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FINISH_WV3 ); + } + else { + code.append( lookup.objMethod->opcodeWV ); + } + } + else { + if ( lookup.objMethod->opcodeWC == IN_PARSE_FINISH_WC ) { + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FINISH_WC ); + code.appendHalf( 0 ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FINISH_WC3 ); + } + else { + code.append( lookup.objMethod->opcodeWC ); + } + } + + if ( lookup.objMethod->useFuncId ) + code.appendHalf( lookup.objMethod->funcId ); +} + +void LangVarRef::popRefQuals( Compiler *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( Compiler *pd, CodeVect &code, ExprVect *args ) +{ + /* 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( Compiler *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( Compiler *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( Compiler *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; + ObjField *field = objDef->findFieldNum( pi.pos() ); + if ( field == 0 ) { + error(fieldInit->loc) << "failed to find init pos " << + pi.pos() << " in object" << endp; + } + + /* Lookup the type of the field and compare it to the type of the + * expression. */ + UniqueType *fieldUT = field->typeRef->uniqueType; + 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::evaluateConstruct( Compiler *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->expr != 0 ) + item->bindId = replacement->nextBindId++; + } + + /* Evaluate variable references. */ + for ( ReplItemList::Iter item = replacement->list->last(); item.gtb(); item-- ) { + if ( item->type == ReplItem::ExprType ) { + UniqueType *ut = item->expr->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->uniqueType; + if ( replUT->typeId != TYPE_TREE ) + error(loc) << "don't know how to construct this type" << endp; + + if ( replUT->langEl->generic != 0 && replUT->langEl->generic->typeId == GEN_PARSER ) { + code.append( IN_CONSTRUCT_INPUT ); + code.append( IN_DUP_TOP_OFF ); + code.appendHalf( 1 ); + code.append( IN_SET_INPUT ); + } + + replacement->langEl = replUT->langEl; + assignFieldArgs( pd, code, replUT ); + + if ( varRef != 0 ) { + code.append( IN_DUP_TOP ); + + /* Get the type of the variable being assigned to. */ + VarRefLookup lookup = varRef->lookupField( pd ); + + varRef->loadObj( pd, code, lookup.lastPtrInQual, false ); + varRef->setField( pd, code, lookup.inObject, replUT, false ); + } + + return replUT; +} + +UniqueType *LangTerm::evaluateParse2( Compiler *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->expr != 0 ) + item->bindId = replacement->nextBindId++; + } + + /* Evaluate variable references. */ + for ( ReplItemList::Iter item = replacement->list->last(); item.gtb(); item-- ) { + if ( item->type == ReplItem::ExprType ) { + UniqueType *ut = item->expr->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 ); + + /* Dup for the send. */ + code.append( IN_DUP_TOP ); + + /* Lookup the type of the replacement and store it in the replacement + * object so that replacement parsing has a target. */ + UniqueType *replUT = typeRef->uniqueType; + if ( replUT->typeId != TYPE_TREE ) + error(loc) << "don't know how to construct this type" << endp; + + if ( replUT->langEl->generic != 0 && replUT->langEl->generic->typeId == GEN_PARSER ) { + code.append( IN_CONSTRUCT_INPUT ); + code.append( IN_DUP_TOP_OFF ); + code.appendHalf( 1 ); + code.append( IN_SET_INPUT ); + } + + replacement->langEl = replUT->langEl; + assignFieldArgs( pd, code, replUT ); + + if ( varRef != 0 ) { + code.append( IN_DUP_TOP ); + + /* Get the type of the variable being assigned to. */ + VarRefLookup lookup = varRef->lookupField( pd ); + + varRef->loadObj( pd, code, lookup.lastPtrInQual, false ); + varRef->setField( pd, code, lookup.inObject, replUT, false ); + } + +/*****************************/ + + /* Assign bind ids to the variables in the replacement. */ + for ( ReplItemList::Iter item = *parserText->list; item.lte(); item++ ) { + switch ( item->type ) { + case ReplItem::FactorType: { + String result; + bool unusedCI; + prepareLitString( result, unusedCI, + item->factor->typeRef->pdaLiteral->data, + item->factor->typeRef->pdaLiteral->loc ); + + /* Make sure we have this string. */ + StringMapEl *mapEl = 0; + if ( pd->literalStrings.insert( result, &mapEl ) ) + mapEl->value = pd->literalStrings.length()-1; + + code.append( IN_LOAD_STR ); + code.appendWord( mapEl->value ); + break; + } + case ReplItem::InputText: { + /* Make sure we have this string. */ + StringMapEl *mapEl = 0; + if ( pd->literalStrings.insert( item->data, &mapEl ) ) + mapEl->value = pd->literalStrings.length()-1; + + code.append( IN_LOAD_STR ); + code.appendWord( mapEl->value ); + break; + } + case ReplItem::ExprType: + item->expr->evaluate( pd, code ); + break; + } + + code.append( IN_DUP_TOP_OFF ); + code.appendHalf( 1 ); + + /* Not a stream. Get the input first. */ + code.append( IN_GET_INPUT ); + if ( pd->revertOn ) + code.append( IN_INPUT_APPEND_WV ); + else + code.append( IN_INPUT_APPEND_WC ); + code.append( IN_POP ); + + code.append( IN_DUP_TOP ); + + /* Parse instruction, dependent on whether or not we are producing + * revert or commit code. */ + if ( pd->revertOn ) { + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FRAG_WV ); + code.appendHalf( 0 ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FRAG_WV3 ); + } + else { + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FRAG_WC ); + code.appendHalf( 0 ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FRAG_WC3 ); + } + } + code.append( IN_POP ); + + return replUT; +} + +UniqueType *LangTerm::evaluateSend( Compiler *pd, CodeVect &code ) const +{ + UniqueType *varUt = varRef->evaluate( pd, code ); + + /* Assign bind ids to the variables in the replacement. */ + for ( ReplItemList::Iter item = *parserText->list; item.lte(); item++ ) { + switch ( item->type ) { + case ReplItem::FactorType: { + String result; + bool unusedCI; + prepareLitString( result, unusedCI, + item->factor->typeRef->pdaLiteral->data, + item->factor->typeRef->pdaLiteral->loc ); + + /* Make sure we have this string. */ + StringMapEl *mapEl = 0; + if ( pd->literalStrings.insert( result, &mapEl ) ) + mapEl->value = pd->literalStrings.length()-1; + + code.append( IN_LOAD_STR ); + code.appendWord( mapEl->value ); + break; + } + case ReplItem::InputText: { + /* Make sure we have this string. */ + StringMapEl *mapEl = 0; + if ( pd->literalStrings.insert( item->data, &mapEl ) ) + mapEl->value = pd->literalStrings.length()-1; + + code.append( IN_LOAD_STR ); + code.appendWord( mapEl->value ); + break; + } + case ReplItem::ExprType: + item->expr->evaluate( pd, code ); + break; + } + + code.append( IN_DUP_TOP_OFF ); + code.appendHalf( 1 ); + + /* Not a stream. Get the input first. */ + code.append( IN_GET_INPUT ); + if ( pd->revertOn ) + code.append( IN_INPUT_APPEND_WV ); + else + code.append( IN_INPUT_APPEND_WC ); + code.append( IN_POP ); + + code.append( IN_DUP_TOP ); + + /* Parse instruction, dependent on whether or not we are producing + * revert or commit code. */ + if ( pd->revertOn ) { + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FRAG_WV ); + code.appendHalf( 0 ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FRAG_WV3 ); + } + else { + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FRAG_WC ); + code.appendHalf( 0 ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FRAG_WC3 ); + } + } + + return varUt; +} + + +UniqueType *LangTerm::evaluateParse( Compiler *pd, CodeVect &code, bool stop ) const +{ + UniqueType *ut = typeRef->uniqueType; + assert( ut != 0 ); + + if ( ut->typeId != TYPE_TREE ) + error(loc) << "can only parse trees" << endl; + + /* Should be one arg, a stream. */ + if ( args == 0 || ( args->length() != 1 && args->length() != 2 ) ) + error(loc) << "expecting one or two args" << endp; + + int context, input; + if ( ut->langEl->contextIn == 0 ) { + if ( args->length() != 1 ) + error(loc) << "parse command requires just input" << endp; + context = -1; + input = 0; + } + else { + if ( args->length() != 2 ) + error(loc) << "parse command requires context and input" << endp; + context = 0; + input = 1; + } + + /* + * Make the parser. + */ + code.append( IN_CONSTRUCT ); + code.appendHalf( replacement->patRepId ); + + /* Dup once for the context load, again for the argument load, again for + * the parse frag, leaving the original there for the finish. */ + code.append( IN_DUP_TOP ); +// code.append( IN_DUP_TOP ); +// code.append( IN_DUP_TOP ); + + /* + * First load the context into the parser. + */ + if ( context < 0 ) { + code.append( IN_LOAD_NIL ); + } + else { + UniqueType *argUT = args->data[context]->evaluate( pd, code ); + if ( argUT != pd->uniqueTypeStream && argUT->typeId != TYPE_TREE ) + error(loc) << "context argument must be a stream or a tree" << endp; + } + + /* FIXME: need to select right one here. */ + code.append( IN_DUP_TOP_OFF ); + code.appendHalf( 1 ); + code.append( IN_SET_PARSER_CTX_WC ); + + /* + * Evaluate the parse arg. + */ + + /* Evaluate the parse args. */ + UniqueType *argUT = args->data[input]->evaluate( pd, code ); + if ( argUT != pd->uniqueTypeStream && argUT->typeId != TYPE_TREE ) + error(loc) << "input argument must be a stream or a tree" << endp; + + /* Allocate a parser id. This will cause a parser to be built for + * the type. */ + if ( ut->langEl->parserId < 0 ) + ut->langEl->parserId = pd->nextParserId++; + + /* If this is a parse stop then we need to verify that the type is + * compatible with parse stop. */ + if ( stop ) + ut->langEl->parseStop = true; + + if ( argUT != pd->uniqueTypeInput ) { + code.append( IN_CONSTRUCT_INPUT ); + if ( pd->revertOn ) + code.append( IN_INPUT_APPEND_WV ); + else + code.append( IN_INPUT_APPEND_WC ); + } + + code.append( IN_DUP_TOP_OFF ); + code.appendHalf( 1 ); + code.append( IN_SET_INPUT ); + + int stopId = stop ? ut->langEl->id : 0; + + /* Parse instruction, dependent on whether or not we are producing revert + * or commit code. */ + if ( pd->revertOn ) { + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FRAG_WV ); + code.appendHalf( stopId ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FRAG_WV3 ); + + /* Finish immediately. */ + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FINISH_WV ); + code.appendHalf( stopId ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FINISH_WV3 ); + } + else { + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FRAG_WC ); + code.appendHalf( stopId ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FRAG_WC3 ); + + /* Finish immediately. */ + code.append( IN_PARSE_SAVE_STEPS ); + code.append( IN_PARSE_LOAD_START ); + code.append( IN_PARSE_FINISH_WC ); + code.appendHalf( stopId ); + code.append( IN_PCR_CALL ); + code.append( IN_PARSE_FINISH_WC3 ); + } + + /* Lookup the type of the replacement and store it in the replacement + * object so that replacement parsing has a target. */ + replacement->langEl = generic->langEl; + + if ( varRef != 0 ) { + code.append( IN_DUP_TOP ); + + /* Get the type of the variable being assigned to. */ + VarRefLookup lookup = varRef->lookupField( pd ); + + varRef->loadObj( pd, code, lookup.lastPtrInQual, false ); + varRef->setField( pd, code, lookup.inObject, ut, false ); + } + + return ut; +} + +UniqueType *LangTerm::evaluateEmbedString( Compiler *pd, CodeVect &code ) const +{ + /* Assign bind ids to the variables in the replacement. */ + for ( ReplItemList::Iter item = *replItemList; item.lte(); item++ ) { + switch ( item->type ) { + case ReplItem::FactorType: { + String result; + bool unusedCI; + prepareLitString( result, unusedCI, + item->factor->typeRef->pdaLiteral->data, + item->factor->typeRef->pdaLiteral->loc ); + + /* Make sure we have this string. */ + StringMapEl *mapEl = 0; + if ( pd->literalStrings.insert( result, &mapEl ) ) + mapEl->value = pd->literalStrings.length()-1; + + code.append( IN_LOAD_STR ); + code.appendWord( mapEl->value ); + break; + } + case ReplItem::InputText: { + /* Make sure we have this string. */ + StringMapEl *mapEl = 0; + if ( pd->literalStrings.insert( item->data, &mapEl ) ) + mapEl->value = pd->literalStrings.length()-1; + + code.append( IN_LOAD_STR ); + code.appendWord( mapEl->value ); + break; + } + case ReplItem::ExprType: + item->expr->evaluate( pd, code ); + break; + } + + } + + long items = replItemList->length(); + for ( long i = 0; i < items-1; i++ ) + code.append( IN_CONCAT_STR ); + + return pd->uniqueTypeStr; +} + +UniqueType *LangTerm::evaluate( Compiler *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 OrigParseType: + return evaluateParse( pd, code, false ); + case OrigParseStopType: + return evaluateParse( pd, code, true ); + case ConstructType: + return evaluateConstruct( pd, code ); + case ParseType: + return evaluateParse2( pd, code ); + case SendType: + return evaluateSend( pd, code ); + case NewType: + return evaluateNew( pd, code ); + case TypeIdType: { + /* Evaluate the expression. */ + UniqueType *ut = typeRef->uniqueType; + 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->uniqueType; + 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; + }; + case EmbedStringType: { + return evaluateEmbedString( pd, code ); + } + } + return 0; +} + +UniqueType *LangExpr::evaluate( Compiler *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 '/': { + UniqueType *lt = left->evaluate( pd, code ); + UniqueType *rt = right->evaluate( pd, code ); + + if ( lt == pd->uniqueTypeInt && rt == pd->uniqueTypeInt ) { + code.append( IN_DIV_INT ); + return pd->uniqueTypeInt; + } + + error(loc) << "do not have an division" + "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 '$': { + right->evaluate( pd, code ); + code.append( IN_TREE_TO_STR ); + return pd->uniqueTypeStr; + + } + case '%': { + right->evaluate( pd, code ); + code.append( IN_TREE_TO_STR_NOTRIM ); + return pd->uniqueTypeStr; + } + case '^': { + UniqueType *rt = right->evaluate( pd, code ); + code.append( IN_TREE_TRIM ); + return rt; + } + 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( Compiler *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->uniqueType; + assert( lookup.uniqueType == lookup.objField->typeRef->uniqueType ); + 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( Compiler *pd, CodeVect &code ) const +{ +// if ( pd->compileContext != Compiler::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( Compiler *pd, CodeVect &code ) const +{ + if ( pd->compileContext != Compiler::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( Compiler *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( Compiler *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 = LangVarRef::cons( loc, "triter" ); + } + else { + error(loc) << "there is no default iterator for a " + "root of that type" << endp; + } + + /* The parameters. */ + ExprVect *callExprVect = new ExprVect; + LangExpr *callExpr = LangExpr::cons( LangTerm::cons( + InputLoc(), LangTerm::VarRefType, fromVarRef->varRef ) ); + callExprVect->append( callExpr ); + + LangTerm *callLangTerm = LangTerm::cons( InputLoc(), callVarRef, callExprVect ); + + return callLangTerm; +} + +void LangStmt::compileForIter( Compiler *pd, CodeVect &code ) const +{ + pd->curLocalFrame->iterPushScope(); + + LangTerm *iterCallTerm = langTerm; + if ( iterCallTerm->type != LangTerm::MethodCallType ) + iterCallTerm = chooseDefaultIter( pd, langTerm ); + + /* The type we are searching for. */ + UniqueType *searchUT = typeRef->uniqueType; + + /* + * 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. */ + UniqueType *iterUniqueType = pd->findUniqueType( TYPE_ITER, lookup.objMethod->iterDef ); + objField->typeRef = TypeRef::cons( loc, lookup.objMethod->iterDef, iterUniqueType, searchUT ); + + /* Also force the field to be initialized. */ + pd->curLocalFrame->initField( pd, objField ); + + /* + * Create the iterator from the local var. + */ + + UniqueType *iterUT = objField->typeRef->uniqueType; + + /* 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; + + pd->curLocalFrame->iterPopScope(); +} + +void LangStmt::compileWhile( Compiler *pd, CodeVect &code ) const +{ + pd->curLocalFrame->iterPushScope(); + + /* 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(); + + pd->curLocalFrame->iterPopScope(); +} + +void LangStmt::compile( Compiler *pd, CodeVect &code ) const +{ + switch ( type ) { + case PrintType: + case PrintXMLACType: + case PrintXMLType: + case PrintStreamType: { + 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 ) { + code.append( IN_PRINT ); + code.append( exprPtrVect->length() ); + } + else if ( type == PrintXMLACType ) { + code.append( IN_PRINT_XML_AC ); + code.append( exprPtrVect->length() ); + } + else if ( type == PrintXMLType ) { + code.append( IN_PRINT_XML ); + code.append( exprPtrVect->length() ); + } + else if ( type == PrintStreamType ) { + /* Minus one because the first arg is the stream. */ + code.append( IN_PRINT_STREAM ); + code.append( exprPtrVect->length() - 1 ); + } + + delete[] types; + + break; + } + case ExprType: { + /* Evaluate the exrepssion, then pop it immediately. */ + expr->evaluate( pd, code ); + code.append( IN_POP ); + break; + } + case IfType: { + pd->curLocalFrame->iterPushScope(); + + long jumpFalse = 0, jumpPastElse = 0, distance = 0; + + /* 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 ); + + pd->curLocalFrame->iterPopScope(); + + if ( elsePart != 0 ) { + /* Compile the else branch. */ + elsePart->compile( pd, code ); + + /* Set the distance for jump over the else part. */ + distance = code.length() - jumpPastElse - 3; + code.setHalf( jumpPastElse+1, distance ); + } + + break; + } + case ElseType: { + pd->curLocalFrame->iterPushScope(); + + /* Compile the else branch. */ + for ( StmtList::Iter stmt = *stmtList; stmt.lte(); stmt++ ) + stmt->compile( pd, code ); + + pd->curLocalFrame->iterPopScope(); + 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 ); + + if ( pd->curFunction == 0 ) { + /* In the main function */ + pd->mainReturnUT = exprUT; + } + else { + UniqueType *resUT = pd->curFunction->typeRef->uniqueType; + 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( Compiler *pd, CodeVect &code ) const +{ + for ( StmtList::Iter stmt = *stmtList; stmt.lte(); stmt++ ) + stmt->compile( pd, code ); +} + +void Compiler::addMatchLength( ObjectDef *frame, LangEl *lel ) +{ + /* Make the type ref. */ + TypeRef *typeRef = TypeRef::cons( 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->insertField( el->name, el ); +} + +void Compiler::addMatchText( ObjectDef *frame, LangEl *lel ) +{ + /* Make the type ref. */ + TypeRef *typeRef = TypeRef::cons( 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->insertField( el->name, el ); +} + +void Compiler::addInput( ObjectDef *frame ) +{ + /* Make the type ref. */ + TypeRef *typeRef = TypeRef::cons( InputLoc(), uniqueTypeInput ); + + /* Create the field and insert it into the map. */ + ObjField *el = new ObjField( InputLoc(), typeRef, "input" ); + el->beenReferenced = true; + el->beenInitialized = true; + el->isConst = false; + el->useOffset = false; + el->isCustom = true; + el->inGetR = IN_LOAD_INPUT_R; + el->inGetWV = IN_LOAD_INPUT_WV; + el->inGetWC = IN_LOAD_INPUT_WC; + frame->insertField( el->name, el ); +} + +void Compiler::addCtx( ObjectDef *frame ) +{ + /* Make the type ref. */ + TypeRef *typeRef = TypeRef::cons( InputLoc(), uniqueTypeStream ); + + /* Create the field and insert it into the map. */ + ObjField *el = new ObjField( InputLoc(), typeRef, "ctx" ); + el->beenReferenced = true; + el->beenInitialized = true; + el->isConst = false; + el->useOffset = false; + el->isCustom = true; + el->inGetR = IN_LOAD_CTX_R; + el->inGetWV = IN_LOAD_CTX_WV; + el->inGetWC = IN_LOAD_CTX_WC; + frame->insertField( el->name, el ); +} + +void Compiler::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 Compiler::initLocalInstructions( ObjField *el ) +{ + el->inGetR = IN_GET_LOCAL_R; + el->inGetWC = IN_GET_LOCAL_WC; + el->inSetWC = IN_SET_LOCAL_WC; +} + +void Compiler::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 Compiler::initIntObject( ) +{ + intObj = new ObjectDef( ObjectDef::BuiltinType, "int", nextObjectId++ ); + intLangEl->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 Compiler::addLengthField( ObjectDef *objDef, Code getLength ) +{ + /* Create the "length" field. */ + TypeRef *typeRef = TypeRef::cons( 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->insertField( el->name, el ); +} + +void Compiler::initStrObject( ) +{ + strObj = new ObjectDef( ObjectDef::BuiltinType, "str", nextObjectId++ ); + strLangEl->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 ); + + initFunction( uniqueTypeStr, globalObjectDef, "sprintf", + IN_SPRINTF, IN_SPRINTF, uniqueTypeStr, uniqueTypeInt, true ); +} + +void Compiler::initStreamObject( ) +{ + streamObj = new ObjectDef( ObjectDef::BuiltinType, + "stream", nextObjectId++ ); + streamLangEl->objectDef = streamObj; +} + +void Compiler::initInputObject( ) +{ + inputObj = new ObjectDef( ObjectDef::BuiltinType, + "accum_stream", nextObjectId++ ); + inputLangEl->objectDef = inputObj; + + initFunction( uniqueTypeStr, inputObj, "pull", + IN_INPUT_PULL_WV, IN_INPUT_PULL_WV, uniqueTypeInt, false ); + initFunction( uniqueTypeStr, inputObj, "push", + IN_INPUT_PUSH_WV, IN_INPUT_PUSH_WV, uniqueTypeAny, false ); + initFunction( uniqueTypeStr, inputObj, "push_ignore", + IN_INPUT_PUSH_IGNORE_WV, IN_INPUT_PUSH_IGNORE_WV, uniqueTypeAny, false ); +} + +ObjField *Compiler::makeDataEl() +{ + /* Create the "data" field. */ + TypeRef *typeRef = TypeRef::cons( 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 *Compiler::makePosEl() +{ + /* Create the "data" field. */ + TypeRef *typeRef = TypeRef::cons( 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; +} + +ObjField *Compiler::makeLineEl() +{ + /* Create the "data" field. */ + TypeRef *typeRef = TypeRef::cons( InputLoc(), uniqueTypeInt ); + ObjField *el = new ObjField( InputLoc(), typeRef, "line" ); + + /* 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_LINE_R; + return el; +} + +void Compiler::initTokenObjects( ) +{ + /* Make a default object Production. */ + tokenObj = new ObjectDef( ObjectDef::BuiltinType, "token", nextObjectId++ ); + + ObjField *dataEl = makeDataEl(); + tokenObj->insertField( dataEl->name, dataEl ); + + ObjField *posEl = makePosEl(); + tokenObj->insertField( posEl->name, posEl ); + + ObjField *lineEl = makeLineEl(); + tokenObj->insertField( lineEl->name, lineEl ); + + /* 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->insertField( dataEl->name, dataEl ); + + /* Create the "pos" field. */ + ObjField *posEl = makePosEl(); + lel->objectDef->insertField( posEl->name, posEl ); + + /* Create the "line" field. */ + ObjField *lineEl = makeLineEl(); + lel->objectDef->insertField( lineEl->name, lineEl ); + } + } + } +} + +void Compiler::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 ( ObjFieldList::Iter ol = *curLocalFrame->objFieldList; ol.lte(); ol++ ) { + ObjField *el = ol->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 || el->isParam ) ) { + UniqueType *ut = el->typeRef->uniqueType; + if ( ut->typeId == TYPE_TREE || ut->typeId == TYPE_PTR ) + trees.insert( el->offset ); + } + } +} + +void Compiler::makeProdCopies( Production *prod ) +{ + int pos = 0; + for ( ProdElList::Iter pel = *prod->prodElList; pel.lte(); pel++, pos++) { + if ( pel->captureField != 0 ) { + prod->copy.append( pel->captureField->offset ); + prod->copy.append( pos ); + } + } +} + +void Compiler::compileReductionCode( Production *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 load right hand side values. */ + addProdRHSLoads( prod, code, afterInit ); + + addProdLHSLoad( prod, code, afterInit ); + addPushBackLHS( prod, code, afterInit ); + + code.append( IN_PCR_RET ); + + /* Now that compilation is done variables are referenced. Make the local + * trees descriptor. */ + findLocalTrees( block->trees ); +} + +void Compiler::compileTranslateBlock( LangEl *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 ); + addInput( curLocalFrame ); + addCtx( curLocalFrame ); + + 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_PCR_RET ); + + /* Now that compilation is done variables are referenced. Make the local + * trees descriptor. */ + findLocalTrees( block->trees ); +} + +void Compiler::compilePreEof( TokenRegion *region ) +{ + CodeBlock *block = region->preEofBlock; + + /* Set up compilation context. */ + compileContext = CompileTranslation; + curLocalFrame = region->preEofBlock->localFrame; + revertOn = true; + block->frameId = nextFrameId++; + + addInput( curLocalFrame ); + addCtx( curLocalFrame ); + + 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_PCR_RET ); + + /* Now that compilation is done variables are referenced. Make the local + * trees descriptor. */ + findLocalTrees( block->trees ); +} + +void Compiler::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 ); + + code.append( IN_LOAD_ARGV ); + code.appendHalf( argvOffset() ); + + 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_STOP ); + + /* Make the local trees descriptor. */ + findLocalTrees( block->trees ); +} + +void Compiler::initAllLanguageObjects() +{ + /* Init all user object fields (need consistent size). */ + for ( LelList::Iter lel = langEls; lel.lte(); lel++ ) { + ObjectDef *objDef = lel->objectDef; + if ( objDef != 0 ) { + /* Init all fields of the object. */ + for ( ObjFieldList::Iter f = *objDef->objFieldList; f.lte(); f++ ) + objDef->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 Compiler::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 Compiler::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 Compiler::initListField( GenericType *gen, const char *name, int offset ) +{ + /* Make the type ref and create the field. */ + TypeRef *typeRef = TypeRef::cons( 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->insertField( el->name, el ); + + el->useOffset = true; + el->beenReferenced = true; + el->beenInitialized = true; + + /* Zero for head, One for tail. */ + el->offset = offset; +} + +void Compiler::initListFields( GenericType *gen ) +{ + initListField( gen, "head", 0 ); + initListField( gen, "tail", 1 ); + initListField( gen, "top", 1 ); +} + +void Compiler::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 Compiler::initParserFunctions( GenericType *gen ) +{ + initFunction( gen->utArg, gen->objDef, "finish", + IN_PARSE_FINISH_WV, IN_PARSE_FINISH_WC, true ); + + initFunction( gen->utArg, gen->objDef, "eof", + IN_PARSE_FINISH_WV, IN_PARSE_FINISH_WC, true ); +} + +void Compiler::initParserField( GenericType *gen, const char *name, int offset ) +{ + /* Make the type ref and create the field. */ + TypeRef *typeRef = TypeRef::cons( InputLoc(), gen->utArg ); + ObjField *el = new ObjField( InputLoc(), typeRef, name ); + + el->inGetR = IN_GET_PARSER_MEM_R; + el->inGetWC = IN_GET_PARSER_MEM_WC; + el->inGetWV = IN_GET_PARSER_MEM_WV; + el->inSetWC = IN_SET_PARSER_MEM_WC; + el->inSetWV = IN_SET_PARSER_MEM_WV; + + gen->objDef->insertField( el->name, el ); + + el->useOffset = true; + el->beenReferenced = true; + el->beenInitialized = true; + + /* Zero for head, One for tail. */ + el->offset = offset; +} + +void Compiler::initCtxField( GenericType *gen ) +{ + LangEl *langEl = gen->utArg->langEl; + Context *context = langEl->contextIn; + + /* Make the type ref and create the field. */ + UniqueType *ctxUT = findUniqueType( TYPE_TREE, context->lel ); + TypeRef *typeRef = TypeRef::cons( InputLoc(), ctxUT ); + ObjField *el = new ObjField( InputLoc(), typeRef, "ctx" ); + + el->inGetR = IN_GET_PARSER_CTX_R; + el->inGetWC = IN_GET_PARSER_CTX_WC; + el->inGetWV = IN_GET_PARSER_CTX_WV; + el->inSetWC = IN_SET_PARSER_CTX_WC; + el->inSetWV = IN_SET_PARSER_CTX_WV; + + gen->objDef->insertField( el->name, el ); + + el->useOffset = false; + el->beenReferenced = true; + el->beenInitialized = true; +} + +void Compiler::initParserFields( GenericType *gen ) +{ + LangEl *langEl = gen->utArg->langEl; + if ( langEl->contextIn != 0 ) + initCtxField( gen ); + + initParserField( gen, "tree", 0 ); +} + +void Compiler::initGenericTypes() +{ + for ( NamespaceList::Iter ns = namespaceList; ns.lte(); ns++ ) { + for ( GenericList::Iter gen = ns->genericList; gen.lte(); gen++ ) { + gen->utArg = gen->typeArg->uniqueType; + + if ( gen->typeId == GEN_MAP ) + gen->keyUT = gen->keyTypeArg->uniqueType; + + gen->objDef = new ObjectDef( ObjectDef::BuiltinType, + gen->name, nextObjectId++ ); + + switch ( gen->typeId ) { + case GEN_MAP: + initMapFunctions( gen ); + break; + case GEN_LIST: + initListFunctions( gen ); + initListFields( gen ); + break; + case GEN_VECTOR: + initVectorFunctions( gen ); + break; + case GEN_PARSER: + /* Need to generate a parser for the type. */ + gen->utArg->langEl->parserId = nextParserId++; + initParserFunctions( gen ); + initParserFields( gen ); + break; + } + + gen->langEl->objectDef = gen->objDef; + } + } +} + +void Compiler::makeFuncVisible( Function *func, bool isUserIter ) +{ + 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->uniqueType; + + if ( func->localFrame->findField( param->name ) != 0 ) + error(param->loc) << "parameter " << param->name << " redeclared" << endp; + + func->localFrame->insertField( param->name, 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->uniqueType : 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 Compiler::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 Compiler::compileUserIter( Function *func ) +{ + CodeBlock *block = func->codeBlock; + + /* Set up the context. */ + compileContext = CompileFunction; + curFunction = func; + block->frameId = nextFrameId++; + + /* Need an object for the local frame. */ + curLocalFrame = func->codeBlock->localFrame; + + /* 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 Compiler::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 ); +} + +void Compiler::compileFunction( Function *func ) +{ + CodeBlock *block = func->codeBlock; + + /* Set up the compilation context. */ + compileContext = CompileFunction; + curFunction = func; + + /* Assign a frame Id. */ + block->frameId = nextFrameId++; + + /* Need an object for the local frame. */ + curLocalFrame = func->codeBlock->localFrame; + + /* 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 Compiler::makeDefaultIterators() +{ + /* Tree iterator. */ + { + UniqueType *anyRefUT = findUniqueType( TYPE_REF, anyLangEl ); + 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, anyLangEl ); + 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, anyLangEl ); + ObjMethod *objMethod = initFunction( uniqueTypeAny, globalObjectDef, + "rev_child", IN_HALT, IN_HALT, anyRefUT, true ); + + IterDef *triter = findIterDef( IterDef::RevChild ); + objMethod->iterDef = triter; + } + + /* Repeat iterator. */ + { + UniqueType *anyRefUT = findUniqueType( TYPE_REF, anyLangEl ); + ObjMethod *objMethod = initFunction( uniqueTypeAny, globalObjectDef, + "repeat", IN_HALT, IN_HALT, anyRefUT, true ); + + IterDef *triter = findIterDef( IterDef::Repeat ); + objMethod->iterDef = triter; + } + + /* Reverse repeat iterator. */ + { + UniqueType *anyRefUT = findUniqueType( TYPE_REF, anyLangEl ); + ObjMethod *objMethod = initFunction( uniqueTypeAny, globalObjectDef, + "rev_repeat", IN_HALT, IN_HALT, anyRefUT, true ); + + IterDef *triter = findIterDef( IterDef::RevRepeat ); + objMethod->iterDef = triter; + } +} + +void Compiler::addStdin() +{ + /* Make the type ref. */ + TypeRef *typeRef = TypeRef::cons( 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->insertField( el->name, el ); +} + +void Compiler::addStdout() +{ + /* Make the type ref. */ + TypeRef *typeRef = TypeRef::cons( 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->insertField( el->name, el ); +} + +void Compiler::addStderr() +{ + /* Make the type ref. */ + TypeRef *typeRef = TypeRef::cons( 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->insertField( el->name, el ); +} + +void Compiler::addArgv() +{ + /* Create the field and insert it into the map. */ + ObjField *el = new ObjField( InputLoc(), argvTypeRef, "argv" ); + el->isArgv = true; + el->isConst = true; + globalObjectDef->insertField( el->name, el ); +} + +int Compiler::argvOffset() +{ + for ( ObjFieldList::Iter field = *globalObjectDef->objFieldList; + field.lte(); field++ ) + { + if ( field->value->isArgv ) { + globalObjectDef->referenceField( this, field->value ); + return field->value->offset; + } + } + assert(false); +} + +void Compiler::initGlobalFunctions() +{ + ObjMethod *method; + + method = initFunction( uniqueTypeStream, globalObjectDef, "open", + IN_OPEN_FILE, IN_OPEN_FILE, uniqueTypeStr, uniqueTypeStr, true ); + method->useCallObj = false; + + method = initFunction( uniqueTypeStr, globalObjectDef, "tolower", + IN_TO_LOWER, IN_TO_LOWER, uniqueTypeStr, true ); + method->useCallObj = false; + + method = initFunction( uniqueTypeStr, globalObjectDef, "toupper", + IN_TO_UPPER, IN_TO_UPPER, uniqueTypeStr, true ); + method->useCallObj = false; + + method = initFunction( uniqueTypeInt, globalObjectDef, "exit", + IN_EXIT, IN_EXIT, uniqueTypeInt, true ); + + method = initFunction( uniqueTypeStr, globalObjectDef, "error", + IN_ERROR, IN_ERROR, true ); + + addStdin(); + addStdout(); + addStderr(); + addArgv(); +} + +void Compiler::removeNonUnparsableRepls() +{ + for ( ReplList::Iter repl = replList; repl.lte(); ) { + Replacement *maybeDel = repl++; + if ( !maybeDel->parse ) + replList.detach( maybeDel ); + } +} + +void Compiler::compileByteCode() +{ +// initUniqueTypes(); + initIntObject(); + initStrObject(); + initStreamObject(); + initInputObject(); + initTokenObjects(); + makeDefaultIterators(); + initAllLanguageObjects(); + initGenericTypes(); + + initGlobalFunctions(); + + for ( FunctionList::Iter f = functionList; f.lte(); f++ ) + makeFuncVisible( f, f->isUserIter ); + + /* This may be comment rot: The function info structure relies on functions + * being compiled first, then iterators. */ + + /* Compile functions. */ + for ( FunctionList::Iter f = functionList; f.lte(); f++ ) { + if ( f->inContext != 0 ) + context = f->inContext; + if ( f->isUserIter ) + compileUserIter( f ); + else + compileFunction( f ); + context = 0; + } + + /* Compile the reduction code. */ + for ( DefList::Iter prod = prodList; prod.lte(); prod++ ) { + makeProdCopies( prod ); + if ( prod->redBlock != 0 ) { + if ( prod->redBlock->context != 0 ) + context = prod->redBlock->context; + compileReductionCode( prod ); + context = 0; + } + } + + /* Compile the token translation code. */ + for ( LelList::Iter lel = langEls; lel.lte(); lel++ ) { + if ( lel->transBlock != 0 ) { + if ( lel->transBlock->context != 0 ) + context = lel->transBlock->context; + compileTranslateBlock( lel ); + context = 0; + } + } + + /* Compile preeof blocks. */ + for ( RegionList::Iter r = regionList; r.lte(); r++ ) { + if ( r->preEofBlock != 0 ) + compilePreEof( r ); + } + + /* Compile the init code */ + compileRootBlock( ); + removeNonUnparsableRepls(); +} |