static void writeNode(A2PWriter writer, A2PType expected, ATermAppl node){ AFun fun = ATgetAFun(node); int arity = ATgetArity(fun); char *name = ATgetName(fun); int i; unsigned int hash = hashString(name); int nodeNameId = ISstore(writer->nameSharingMap, (void*) name, hash); if(nodeNameId == -1){ int nameLength = dataArraySize(name); writeByteToBuffer(writer->buffer, PDB_NODE_HEADER); printInteger(writer->buffer, nameLength); writeDataToBuffer(writer->buffer, name, nameLength); }else{ writeByteToBuffer(writer->buffer, PDB_NODE_HEADER | PDB_NAME_SHARED_FLAG); printInteger(writer->buffer, nodeNameId); } printInteger(writer->buffer, arity); for(i = 0; i < arity; i++){ doSerialize(writer, A2PvalueType(), ATgetArgument(node, i)); } }
static void writeADT(A2PWriter writer, A2PType expected, ATerm value){ int termType = ATgetType(value); if(termType == AT_APPL){ ATermAppl appl = (ATermAppl) value; AFun fun = ATgetAFun(appl); char *name = ATgetName(fun); int arity = ATgetArity(fun); A2PType constructorType = A2PlookupConstructorType(expected, name, arity); if(constructorType == NULL){ fprintf(stderr, "Unable to find a constructor that matches the given ADT type. Name: %s, arity: %d, ADT name: %s.\n", name, arity, ((A2PAbstractDataType) expected->theType)->name); exit(1); } writeConstructor(writer, constructorType, appl); }else{ A2PType wrapper; switch(termType){ case AT_INT: wrapper = A2PlookupConstructorWrapper(expected, A2PintegerType()); break; case AT_REAL: wrapper = A2PlookupConstructorWrapper(expected, A2PrealType()); break; default: fprintf(stderr, "The given ATerm of type: %d, can not be a constructor.\n", termType); exit(1); } if(wrapper == NULL){ fprintf(stderr, "Unable to find constructor wrapper for ATerm with type : %d.\n", termType); exit(1);} writeConstructor(writer, wrapper, ATmakeAppl1(ATmakeAFun(((A2PConstructorType) wrapper->theType)->name, 1, ATfalse), value)); } }
static void writeConstructor(A2PWriter writer, A2PType expected, ATermAppl constructor){ A2PConstructorType t = (A2PConstructorType) expected->theType; A2PTupleType children = ((A2PTupleType) t->children->theType); int nrOfChildren = typeArraySize(children->fieldTypes); ISIndexedSet sharedTypes = writer->typeSharingMap; int typeHash = hashType(expected); int constructorTypeId = ISget(sharedTypes, (void*) expected, typeHash); int arity = ATgetArity(ATgetAFun(constructor)); int i; if(arity != nrOfChildren){ fprintf(stderr, "Arity (%d) is unequal to the number of children (%d); term was:\n%s\n", arity, nrOfChildren, ATwriteToString((ATerm) constructor)); exit(1);} if(constructorTypeId == -1){ writeByteToBuffer(writer->buffer, PDB_CONSTRUCTOR_HEADER); doWriteType(writer, expected); ISstore(sharedTypes, (void*) expected, typeHash); }else{ writeByteToBuffer(writer->buffer, PDB_CONSTRUCTOR_HEADER | PDB_TYPE_SHARED_FLAG); printInteger(writer->buffer, constructorTypeId); } printInteger(writer->buffer, arity); for(i = 0; i < arity; i++){ doSerialize(writer, children->fieldTypes[i], ATgetArgument(constructor, i)); } }
static ATerm CaseRewrite(ATerm t) { AFun f = ATgetAFun(t); int n = ATgetArity(f); ATerm u; // ATwarning("%t", t); if (n==0) return tasks->RWrewrite(t); u = ATtableGet(norm, t); if (u) return u; { int i; ATerm *a = calloc(n, sizeof(ATerm)); ATbool changed = ATfalse; ATprotectArray(a, n); for (i=0; i<n; i++) { ATerm arg = ATgetArgument((ATermAppl) t, i); a[i] = CaseRewrite(arg); if (!ATisEqual(a[i], arg)) changed = ATtrue; } u = CaseRewriteStep(changed?(ATerm) ATmakeApplArray(f, a):t); ATtablePut(norm, t, u); ATunprotect(a); free(a); return u; } }
static ATerm unfold_rec(ATerm t) { /* Completely unfolds t according to equivalence. invariants: - loop_detection contains "ancestors" of t - t is end point of find - solution contains correct results [t -> s] returns NULL: loop detected returns s: s is unfolding of t. */ ATerm s; ATbool no_loop; char unifiable=1; if (ATisVariable(t)) return t; if ((s=ATtableGet(solution,t))) return s; ATindexedSetPut(loop_detection,t,&no_loop); if (no_loop) { Symbol sym = ATgetSymbol(t); int i,n=ATgetArity(sym); ATerm *args = (ATerm*)alloca(n*sizeof(ATerm)); for (i=0;i<n;i++) if (!(args[i] = unfold_rec(find(ATgetArgument(t,i))))) { unifiable=0; break; } ATindexedSetRemove(loop_detection,t); if (unifiable) { s=(ATerm)ATmakeApplArray(sym,args); ATtablePut(solution,t,s); return s; } } /* here either !no_loop, or !unifiable holds */ return NULL; }
static void writeAnnotatedNode(A2PWriter writer, A2PType expected, ATermAppl node, ATermList annotations){ A2PNodeType t = (A2PNodeType) expected->theType; AFun fun = ATgetAFun(node); int arity = ATgetArity(fun); char *name = ATgetName(fun); int nrOfAnnotations = ATgetLength(annotations); int i; ATerm annotationLabel; ATerm annotationValue; unsigned int hash = hashString(name); int nodeNameId = ISstore(writer->nameSharingMap, (void*) name, hash); if(nodeNameId == -1){ int nameLength = dataArraySize(name); writeByteToBuffer(writer->buffer, PDB_ANNOTATED_NODE_HEADER); printInteger(writer->buffer, nameLength); writeDataToBuffer(writer->buffer, name, nameLength); }else{ writeByteToBuffer(writer->buffer, PDB_ANNOTATED_NODE_HEADER | PDB_NAME_SHARED_FLAG); printInteger(writer->buffer, nodeNameId); } printInteger(writer->buffer, arity); for(i = 0; i < arity; i++){ doSerialize(writer, A2PvalueType(), ATgetArgument(node, i)); } /* Annotations. */ if((nrOfAnnotations % 2) == 1){ fprintf(stderr, "Detected corrupt annotations (Unbalanced).\n"); exit(1); } printInteger(writer->buffer, nrOfAnnotations); do{ char *label; int labelLength; A2PType annotationType; annotationLabel = ATgetFirst(annotations); annotations = ATgetNext(annotations); annotationValue = ATgetFirst(annotations); annotations = ATgetNext(annotations); if(ATgetType(annotationLabel) != AT_APPL){ fprintf(stderr, "Detected corrupt annotation; label term is not a 'string'.\n"); exit(1); } label = ATgetName(ATgetAFun((ATermAppl) annotationLabel)); labelLength = dataArraySize(label); printInteger(writer->buffer, labelLength); writeDataToBuffer(writer->buffer, label, labelLength); annotationType = (A2PType) HTget(t->declaredAnnotations, (void*) label, hashString(label)); doSerialize(writer, annotationType, annotationValue); }while(!ATisEmpty(annotations)); }
static void writeAnnotatedConstructor(A2PWriter writer, A2PType expected, ATermAppl constructor, ATermList annotations){ A2PConstructorType t = (A2PConstructorType) expected->theType; ISIndexedSet sharedTypes = writer->typeSharingMap; int typeHash = hashType(expected); int constructorTypeId = ISget(sharedTypes, (void*) expected, typeHash); int arity = ATgetArity(ATgetAFun(constructor)); int nrOfAnnotations = ATgetLength(annotations); int i; ATerm annotationLabel; ATerm annotationValue; if(constructorTypeId == -1){ writeByteToBuffer(writer->buffer, PDB_ANNOTATED_CONSTRUCTOR_HEADER); doWriteType(writer, expected); ISstore(sharedTypes, (void*) expected, typeHash); }else{ writeByteToBuffer(writer->buffer, PDB_ANNOTATED_CONSTRUCTOR_HEADER | PDB_TYPE_SHARED_FLAG); printInteger(writer->buffer, constructorTypeId); } printInteger(writer->buffer, arity); for(i = 0; i < arity; i++){ doSerialize(writer, ((A2PTupleType) t->children->theType)->fieldTypes[i], ATgetArgument(constructor, i)); } /* Annotations. */ if((nrOfAnnotations % 2) == 1){ fprintf(stderr, "Detected corrupt annotations (Unbalanced).\n"); exit(1); } printInteger(writer->buffer, nrOfAnnotations); do{ char *label; int labelLength; A2PType annotationType; annotationLabel = ATgetFirst(annotations); annotations = ATgetNext(annotations); annotationValue = ATgetFirst(annotations); annotations = ATgetNext(annotations); if(ATgetType(annotationLabel) != AT_APPL){ fprintf(stderr, "Detected corrupt annotation; label term is not a 'string'.\n"); exit(1); } label = ATgetName(ATgetAFun((ATermAppl) annotationLabel)); labelLength = dataArraySize(label); printInteger(writer->buffer, labelLength); writeDataToBuffer(writer->buffer, label, labelLength); annotationType = (A2PType) HTget(t->declaredAnnotations, (void*) label, hashString(label)); doSerialize(writer, annotationType, annotationValue); }while(!ATisEmpty(annotations)); }
void RWdeclareVariables(ATermList variable_names) { if (ATisEmpty(variable_names)) return; tasks->RWdeclareVariables(variable_names); if (!currentAdt) return; if (ATgetArity(ATgetAFun(ATgetFirst(variable_names)))==0) MCRLdeclareVarNames(variable_names); else MCRLdeclareVars(variable_names); }
ATerm funcAbstraction(ATerm func, ATerm dstSort){ ATermList argSorts; ATerm newTerm, newTermSort; ATerm arg, argSort, fSort, argSortAux; ATbool modified; int i, j; char *fName; AFun fun; argSorts = getFuncSortList(func); fSort = getTermSort(func); fun = ATgetAFun(func); fName = ATgetName(fun); if(reservedFunc(fun)) return func; do{ modified = ATfalse; for(i=0; i< ATgetArity(ATgetAFun(func)); i++){ arg = ATgetArgument((ATermAppl) func, i); argSort = ATelementAt(argSorts, i); if(toAbstractArg(argSort, argSorts, fSort)) argSort = liftSort(abstractSort(getUnLifted(argSort))); newTerm = termAbstraction(arg, argSort); newTermSort = getTermSort(newTerm); if(newTerm != arg) modified = ATtrue; func = (ATerm) ATsetArgument((ATermAppl) func, newTerm, i); argSorts = ATreplace(argSorts, newTermSort, i); if(toAbstractTarget(newTermSort, fSort)) fSort = liftSort(abstractSort(getUnLifted(fSort))); if(toLiftTarget(newTermSort, fSort)) fSort = liftSort(fSort); if(modified) break; } } while(modified); if(toAbstractSort(fSort) && abstractedSorts(argSorts)) fSort = liftSort(abstractSort(getUnLifted(fSort))); func = createNewFuncTerm(func, argSorts, fSort); return func; }
ATbool ATunifySystem(ATermStack system,ATermSubst sigma) { /* Solves {system[0]=system[1], ...,system[2n-2]=system[2n-1]} - returns 0: t1=t2 is not unifiable; sigma is reset. - returns 1: ATermSubst represents the mgu {X1->t1,..,Xn->tn} This implements the Pascal version of Baader/Nipkow. (Linear space, nearly linear time) - ATermTable equivalence contains the Union/Find structure - ATermStack assigned contains the domain of the solution First, the system is solved without occur-check Subsequently, a substitution is computed, with loop detection. */ static char first_call = 1; char unifiable = 1; if (first_call) { first_call=0; assigned = ATstackCreate(40); equivalence = ATtableCreate(40,40); } assert((!sigma) || (ATstackDepth(sigma)==0)); assert(ATstackDepth(assigned)==0); assert(ATisEmpty(ATtableKeys(equivalence))); while (ATstackDepth(system)>0) { ATerm t1=find(ATstackPop(system)); ATerm t2=find(ATstackPop(system)); int i,n; if (t1==t2) continue; if (ATisVariable(t2)) { ATerm t3=t1; t1=t2; t2=t3; } if (ATisVariable(t1)) { ATstackPush(assigned,t1); ATtablePut(equivalence,t1,t2); /* ATprintf("%t->%t\n",t1,t2); */ } else { /* both t1 and t2 start with function symbol. */ Symbol s1 = ATgetSymbol(t1); Symbol s2 = ATgetSymbol(t2); if (s1!=s2) { unifiable=0; break; } else { n = ATgetArity(s1); ATtablePut(equivalence,t1,t2); /* note: forget about cardinality */ for (i=0;i<n;i++) { ATstackPush(system,ATgetArgument(t1,i)); ATstackPush(system,ATgetArgument(t2,i)); } } } } if (unifiable) return unfold_solution(sigma); else { ATstackReset(system); ATstackReset(assigned); ATtableReset(equivalence); return ATfalse; } }
static void checkAlias(ATerm alias) { if (ATgetType(alias) == AT_APPL) { ATermAppl appl = (ATermAppl)alias; AFun afun = ATgetAFun(appl); if (ATgetArity(afun) == 0 && !ATisQuoted(afun)) { return; } } ATfprintf(stderr, "incorrect alias: %t\n", alias); exit(1); }
ATerm CO_unquoteAppl(ATerm appl) { AFun fun; int arity; char *name = NULL; ATermList args = NULL; assert(ATgetType(appl) == AT_APPL); fun = ATgetAFun((ATermAppl) appl); arity = ATgetArity(fun); name = ATgetName(fun); args = ATgetArguments((ATermAppl) appl); fun = ATmakeAFun(name, arity, ATfalse); return (ATerm) ATmakeApplList(fun, args); }
static ATbool occurs_rec(ATerm var, ATerm t, ATbool *nonempty) { /* invariants: - var doesn't occur in terms in No_occurs - nonempty iff No_occurs is not empty */ ATbool b; if (var==t) return ATtrue; else if (ATisVariable(t)) return ATfalse; else if (*nonempty && ATindexedSetGetIndex(No_occurs,t)>=0) return ATfalse; else { int i; for (i=ATgetArity(ATgetSymbol(t))-1;i>=0;i--) if (occurs_rec(var, ATgetArgument(t,i),nonempty)) return ATtrue; } *nonempty = ATtrue; ATindexedSetPut(No_occurs,t,&b); return ATfalse; }
static void writeTuple(A2PWriter writer, A2PType expected, ATermAppl tuple){ A2PTupleType t = (A2PTupleType) expected->theType; A2PType *fieldTypes = t->fieldTypes; int numberOfFieldTypes = typeArraySize(fieldTypes); int arity = ATgetArity(ATgetAFun(tuple)); int i; if(numberOfFieldTypes != arity){ fprintf(stderr, "The number of children specified in the type is not equal to the arity of this tuple.\n"); exit(1); } writeByteToBuffer(writer->buffer, PDB_TUPLE_HEADER); printInteger(writer->buffer, arity); for(i = 0; i < arity; i++){ doSerialize(writer, fieldTypes[i], ATgetArgument(tuple, i)); } }
ATbool toLiftFunc(ATerm func){ ATerm arg; int i; ATermList argSorts, args; ATerm fSort, argSort; fSort = getTermSort(func); argSorts = getFuncSortList(func); if(toAbstractSort(fSort)) return ATtrue; for(i=0; i< ATgetArity(ATgetAFun(func)); i++){ arg = ATgetArgument((ATermAppl) func, i); if(toLiftTerm(arg)) return ATtrue; } return ATfalse; }
static ATerm substVar_rec(ATerm t, ATerm var, ATerm s, ATbool *nonempty) { /* invariants: - Subst contains pairs (r , r[var := s]) - *nonempty iff Subst is not empty */ ATerm r; if (var == t) return s; else if (ATisVariable(t)) return t; else if (*nonempty && (r=ATtableGet(Subst,t))) return r; else { Symbol sym = ATgetSymbol(t); int i,n=ATgetArity(sym); ATerm* args = (ATerm*)alloca(n*sizeof(ATerm)); for (i=0;i<n;i++) args[i]=substVar_rec(ATgetArgument(t,i),var,s,nonempty); r = (ATerm)ATmakeApplArray(sym,args); *nonempty=ATtrue; ATtablePut(Subst,t,r); return r; } }
static void serializeUntypedTerm(A2PWriter writer, ATerm value){ int type = ATgetType(value); switch(type){ case AT_INT: writeInteger(writer, (ATermInt) value); break; case AT_REAL: writeDouble(writer, (ATermReal) value); break; case AT_APPL: { ATermAppl appl = (ATermAppl) value; AFun fun = ATgetAFun(appl); if(ATisQuoted(fun) == ATfalse){ A2PType expected = A2PnodeType(); ATermList annotations = (ATermList) ATgetAnnotations(value); if(annotations == NULL){ writeNode(writer, expected, appl); }else{ if(((A2PNodeType) expected->theType)->declaredAnnotations == NULL){ fprintf(stderr, "Node term has annotations, but none are declared.\n"); exit(1); } writeAnnotatedNode(writer, expected, appl, annotations); } }else{ if(ATgetArity(fun) != 0){ fprintf(stderr, "Quoted appl (assumed to be a string) has a non-zero arity.\n"); exit(1);} writeString(writer, appl); } } break; case AT_LIST: writeList(writer, A2PlistType(A2PvalueType()), (ATermList) value); break; default: fprintf(stderr, "Encountered unwriteable type: %d.\n", type); exit(1); } }
static ATerm substitute_rec(ATerm t, ATermSubst sigma, ATbool *nonempty) { /* invariants: - Subst contains pairs (r , r^sigma) - *nonempty iff Subst is not empty */ ATerm s; if (ATisVariable(t)) { s=ATstackGet(sigma,ATgetInt((ATermInt)t)); return (s ? s : t); } else if (*nonempty && (s=ATtableGet(Subst,t))) return s; else { Symbol sym = ATgetSymbol(t); int i,n=ATgetArity(sym); ATerm* args = (ATerm*)alloca(n*sizeof(ATerm)); for (i=0;i<n;i++) args[i]=substitute_rec(ATgetArgument(t,i),sigma,nonempty); s = (ATerm)ATmakeApplArray(sym,args); *nonempty=ATtrue; ATtablePut(Subst,t,s); return s; } }
unsigned int calc_hash(ATerm t) { unsigned int hnr = 0; switch(ATgetType(t)) { case AT_APPL: { ATermAppl appl = (ATermAppl)t; AFun sym = ATgetAFun(appl); int i, arity = ATgetArity(sym); hnr = AT_hashSymbol(ATgetName(sym), arity); for(i=0; i<arity; i++) { hnr = hnr * MAGIC_HASH_CONST_APPL + calc_hash(ATgetArgument(appl, i)); } } break; case AT_INT: hnr = ATgetInt((ATermInt)t); break; case AT_LIST: { ATermList list = (ATermList)t; hnr = 123; while(!ATisEmpty(list)) { hnr = hnr * MAGIC_HASH_CONST_LIST + calc_hash(ATgetFirst(list)); list = ATgetNext(list); } } break; } return hnr; }
ATbool isConstant(ATerm term){ return (ATgetArity(ATgetAFun(term)) == 0); }
int doConvert(FILE *fpOut, int traceLevel) { SVCstateIndex fromState, toState; SVClabelIndex label/*,taulabel*/; SVCparameterIndex parameter; SVCbool tempbool=0; char name[256], *first, *last; if (traceLevel>0){ /* Get file header info */ fprintf(stderr, "Svc version %s %s\n", SVCgetFormatVersion(&inFile), SVCgetIndexFlag(&inFile)?"(indexed)":""); fprintf(stderr, " filename %s\n", SVCgetFilename(&inFile)); fprintf(stderr, " date %s\n", SVCgetDate(&inFile)); fprintf(stderr, " version %s\n", SVCgetVersion(&inFile)); fprintf(stderr, " type %s\n", SVCgetType(&inFile)); fprintf(stderr, " creator %s\n", SVCgetCreator(&inFile)); fprintf(stderr, " states %ld\n", SVCnumStates(&inFile)); fprintf(stderr, " transitions %ld\n", SVCnumTransitions(&inFile)); fprintf(stderr, " labels %ld\n", SVCnumLabels(&inFile)); fprintf(stderr, " parameters %ld\n", SVCnumParameters(&inFile)); ATfprintf(stderr, " initial state %t\n", SVCstate2ATerm(&inFile,SVCgetInitialState(&inFile))); fprintf(stderr, " comments %s\n", SVCgetComments(&inFile)); } /* base name of input file */ strncpy(name, SVCgetFilename(&inFile), 256); first = strrchr(name,'/'); if (!first) first = name; last = strrchr(first,'.'); if (last && !strcmp(last,INFILE_EXT)) *last ='\0'; /* fprintf(fpOut, "des(%ld,%ld,%ld)\n", SVCgetInitialState(&inFile), SVCnumTransitions(&inFile), SVCnumStates(&inFile)); while (SVCgetNextTransition(&inFile, &fromState, &label, &toState, ¶meter)) { ATfprintf(fpOut, "(%d, %t, %d)\n", fromState, SVClabel2ATerm(&inFile,label), toState); } */ /* adaption to output dot files (by Jan Friso Groote) */ /* taulabel=SVCnewLabel(&inFile,ATmake("\"tau\""),&tempbool); if (tempbool) { *//* the label tau did not exist yet, let's try i */ /* taulabel=SVCnewLabel(&inFile,ATmake("\"i\""),&tempbool); if (tempbool) { *//* the label i does not exist also, so forget about tau's */ /* taulabel=0; } }*/ fprintf(fpOut,"digraph \"%s\" {\n", first); /* fprintf(fpOut,"size=\"7,10.5\";\n"); */ fprintf(fpOut,"center=TRUE;\n"); fprintf(fpOut,"mclimit=10.0;\n"); fprintf(fpOut,"nodesep=0.05;\n"); fprintf(fpOut,"node[width=0.25,height=0.25,label=\"\"];\n"); fprintf(fpOut,"%d[peripheries=2];\n",SVCgetInitialState(&inFile)); while (SVCgetNextTransition(&inFile, &fromState, &label, &toState, ¶meter)) { ATerm t = SVClabel2ATerm(&inFile,label); AFun s = ATgetAFun(t); if (ATisQuoted(s) && ATgetArity(s)==0) ATfprintf(fpOut, "%d->%d[label=%t];\n",fromState, toState, t); else ATfprintf(fpOut, "%d->%d[label=\"%t\"];\n",fromState, toState, t); /* if (label==taulabel) ATfprintf(fpOut,"color=\"grey\",fontsize=\"18\"]\n"); else ATfprintf(fpOut,"fontsize=\"32\", weight=\"4\"]\n"); */ } fprintf(fpOut,"}\n"); if (SVCclose(&inFile)<0){ if (traceLevel>0) { fprintf(stderr, "File trailer corrupt...\n"); } return -1; } fclose(fpOut); return EXIT_OK; } /* doConvert */
CAESAR_TYPE_NATURAL CAESAR_CARDINAL_LABEL(CAESAR_TYPE_LABEL l) { return ATgetArity(ATgetSymbol(ATparse(CAESAR_STRING_LABEL(l)))); }
static void term2buf(ATerm t) { ATerm annos = AT_getAnnotations(t); if(annos != NULL) { char2buf('{'); } switch(ATgetType(t)) { case AT_INT: wprintf("%d", ATgetInt((ATermInt)t)); break; case AT_REAL: wprintf("%f", ATgetReal((ATermReal)t)); break; case AT_APPL: { int cur_arg, arity; ATermAppl appl = (ATermAppl)t; AFun sym = ATgetSymbol(appl); if(ATisQuoted(sym)) qstr2buf(ATgetName(sym)); else str2buf(ATgetName(sym)); arity = ATgetArity(sym); if(arity > 0) { char2buf('('); for(cur_arg=0; cur_arg<arity; cur_arg++) { term2buf(ATgetArgument(appl, cur_arg)); if(cur_arg < (arity-1)) char2buf(','); } char2buf(')'); } } break; case AT_LIST: { ATermList l = (ATermList)t; char2buf('{'); while(!ATisEmpty(l)) { ATerm el = ATgetFirst(l); l = ATgetNext(l); term2buf(el); if(!ATisEmpty(l)) char2buf(' '); } char2buf('}'); } break; case AT_PLACEHOLDER: { char2buf('<'); term2buf(ATgetPlaceholder((ATermPlaceholder)t)); char2buf('>'); } break; case AT_BLOB: ATerror("blobs are not supported by tcltk-adapter!\n"); default: ATabort("illegal term type!\n"); } if(annos != NULL) { char2buf(' '); term2buf(annos); char2buf('}'); } }