DNA.
wyrm-sym
Version.
1.1.9
Namespace.
::wyrm
Command.
::wyrm::sym
Language.
c
Manpage.
sym (1WY)
Manpage.
grammar (1WY)
Testbase.
Test Script
Test Report
Import.
Interface.
wyrm-io
wyrmwif
wyrm-assoc
wyrm-oav
Package.
wyrmwif
wyrm-assoc
Export.
Implementation.
wyrm-sym.c
Interface.
wyrm-sym.h
Package.
wyrmsym.dylib

Symbol Strings and Sets

Sections.
Symbol Strings and Sets
Symbol Strings
Symbol String Set
Directed Graphs
Grammar Command
Make.
Package.
compile -cc -ld -o [
  export package
] [
  export implementation
] -- -list [import interface] [export interface]
Script.
rule wyrm-sym.package $so/wyrmsym[info sharedlibextension]
rule wyrm-sym.test [list \
    pkgIndex.tcl \
    $test/wyrm-sym.test.html \
]

rule clean :: {} "
  -rm $test/wyrm-sym.TESTING
"
rule clobber :: {} "
  -rm $include/wyrm-sym.h
  -rm $so/wyrmsym[info sharedlibextension]
"
   
top

1 :: Symbol Strings and Sets.

Copyright (C) 2002 SM Ryan

Wyrmwif Tcl extensions. For non-profit uses only, provided this copyright is preserved on all copies, this work may be freely copied, modified, redistributed, compiled, and incorporated in other works. This work is distributed with no warranty of any kind; no author or distributor accepts any responsibility for the consequences of using it, or for whether it serves any particular purpose or works at all, unless he or she says so in writing.

   
   

Symbol Strings and Sets

   
top
2. sym (1WY) ::

NAME

sym — Symbol string and set operations.

synopsis

wyrm::sym operation parameter...

 
section    top

#ifndef WYRM_SYM_H
#define WYRM_SYM_H

	//	wyrm-sym.dna - Copyright (C) 2002 SM Ryan.  All rights reserved.

	#include "wyrmwif.h"

	<External declarations>

#endif

		
 
section    top

static const char COPYRIGHT[] = "wyrm-sym.dna - Copyright (C) 2002 SM Ryan.  All rights reserved.";

#include <ctype.h>


#include "wyrm-sym.h"
#include "wyrm-assoc.h"
#include "wyrm-oav.h"
#include "wyrm-io.h"

<Typedef declarations>
<Forward declarations>
<Symbol string type>
<Symbol set type>
<Symbol graph type>
<SCC cache>
<SymbolString Tcl type>
<Symbol set Tcl type>
<SymbolGraph Tcl type>
<Symbol string operations>
<Symbol string set operations>
<Directed graph operations>
<Symbol string command>
<Symbol string package>
<grammar (1WY)>
			
		
 
section    top
proc symCommand—Evaluate the sym command; returns TCL_OK or TCL_ERROR.
output intr—Command results or error message.
input N—Number of parameters.
input P—Parameters.

static int symCommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	int index;
	enum {
			o_encode,o_decode,
			o_empty,o_length,o_first,o_rest,o_last,o_front,o_index,o_range,o_reverse,o_label,o_unlabel,
				o_enum,
			o_null,o_card,o_intersect,o_union,o_diff,o_concat,o_contains,o_equals,
				o_prefix,o_suffix,o_each,o_choose,
				o_nest,o_unnest,o_nested,
			o_vertex,o_edge,o_from,o_to,o_invert,o_subgraph,o_scc,o_member,o_factor,o_tsort,
			o_grammar,
			o_END
	};
	static char const *operator[] = {
			"encode","decode",
			"empty","length","first","rest","last","front","index","range","reverse","label","unlabel",
				"enum",
			"null","card","intersect","union","diff","concat","contains","equals",
				"prefix","suffix","each","choose",
				"nest","unnest","nested",
			"vertex","edge","from","to","invert","subgraph","scc","member","factor","tsort",
			"grammar",
			0
	};
	if (N<=1) {Tcl_WrongNumArgs(intr,1,P,"subcommand"); return TCL_ERROR;}
	if (Tcl_GetIndexFromObj(intr,P[1],operator,"operator",0,&index)!=TCL_OK) return TCL_ERROR;
	switch (index) {
		case o_encode: {
			<sym encode syntax>
			<sym encode>
		}	break;
		case o_decode: {
			<sym decode syntax>
			<sym decode>
		}	break;
		case o_empty: {
			<sym empty syntax>
			<sym empty>
		}	break;
		case o_length: {
			<sym length syntax>
			<sym length>
		}	break;
		case o_first: {
			<sym first syntax>
			<sym first>
		}	break;
		case o_rest: {
			<sym rest syntax>
			<sym rest>
		}	break;
		case o_last: {
			<sym last syntax>
			<sym last>
		}	break;
		case o_front: {
			<sym front syntax>
			<sym front>
		}	break;
		case o_index: {
			<sym index syntax>
			<sym index>
		}	break;
		case o_range: {
			<sym range syntax>
			<sym range>
		}	break;
		case o_reverse: {
			<sym reverse syntax>
			<sym reverse>
		}	break;
		case o_label: {
			<sym label syntax>
			<sym label>
		}	break;
		case o_unlabel: {
			<sym unlabel syntax>
			<sym unlabel>
		}	break;
		case o_enum: {
			<sym enum syntax>
			<sym enum>
		}	break;
		case o_null: {
			<sym null syntax>
			<sym null>
		}	break;
		case o_card: {
			<sym card syntax>
			<sym card>
		}	break;
		case o_intersect: {
			<sym intersect syntax>
			<sym intersect>
		}	break;
		case o_union: {
			<sym union syntax>
			<sym union>
		}	break;
		case o_diff: {
			<sym diff syntax>
			<sym diff>
		}	break;
		case o_concat: {
			<sym concat syntax>
			<sym concat>
		}	break;
		case o_contains: {
			<sym contains syntax>
			<sym contains>
		}	break;
		case o_equals: {
			<sym equals syntax>
			<sym equals>
		}	break;
		case o_prefix: {
			<sym prefix syntax>
			<sym prefix>
		}	break;
		case o_suffix: {
			<sym suffix syntax>
			<sym suffix>
		}	break;
		case o_each: {
			<sym each syntax>
			<sym each>
		}	break;
		case o_choose: {
			<sym choose syntax>
			<sym choose>
		}	break;
		case o_nest: {
			<sym nest syntax>
			<sym nest>
		}	break;
		case o_unnest: {
			<sym unnest syntax>
			<sym unnest>
		}	break;
		case o_nested: {
			<sym nested syntax>
			<sym nested>
		}	break;
		case o_vertex: {
			<sym vertex syntax>
			<sym vertex>
		}	break;
		case o_edge: {
			<sym edge syntax>
			<sym edge>
		}	break;
		case o_from: {
			<sym from syntax>
			<sym from>
		}	break;
		case o_to: {
			<sym to syntax>
			<sym to>
		}	break;
		case o_invert: {
			<sym invert syntax>
			<sym invert>
		}	break;
		case o_subgraph: {
			<sym subgraph syntax>
			<sym subgraph>
		}	break;
		case o_scc: {
			<sym scc syntax>
			<sym scc>
		}	break;
		case o_member: {
			<sym memb syntax>
			<sym memb>
		}	break;
		case o_factor: {
			<sym factor syntax>
			<sym factor>
		}	break;
		case o_tsort: {
			<sym tsort syntax>
			<sym tsort>
		}	break;
		case o_grammar: {
			<sym grammar syntax>
			<sym grammar>
		}
	}
	rprintf(intr,"you can't here from there: %{y}s",P[1]); return TCL_ERROR;
}

		
 
section    top
static int symCommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]);
		
 
section    top
proc Wyrmsym_Init—wyrmsym package initialisation; returns TCL_OK or TCL_ERROR.
output intr—The wyrmsym package is initialised, or error message.

int Wyrmsym_Init(Intr intr) {
	Tcl_PkgProvide(intr,"wyrmsym",VERSION);
	Tcl_PkgRequire(intr,"wyrmwif","1",0);
	Tcl_PkgRequire(intr,"wyrmassoc","1",0);
	Tcl_CreateObjCommand(intr,"::wyrm::sym",symCommand,0,0);
	Tcl_RegisterObjType(&SymbolsType);
	Tcl_RegisterObjType(&SetType);
	Tcl_RegisterObjType(&GraphType);
	return Tcl_VarEval(intr,
			"namespace eval ::wyrm {namespace export sym}\n",
			0);
}

int Wyrmsym_SafeInit(Intr intr) {
	return Wyrmsym_Init(intr);
}

		
 
section    top
int Wyrmsym_Init(Intr intr);
int Wyrmsym_SafeInit(Intr intr);
		
   
   

Symbol Strings

   
top
 
section    top

10. Symbol string type :: The symbol strings manipulated by these commands have the characteristics

Once a string is created (by sym ensym, by interpretting the string interpretation, or by concatenation sym concat) it is not thereafter modified by adding symbols, modifying symbols, or removing symbols from the interior.
The string will need to be traversed front to back or back to front efficiently.

Internally a symbol string is a shared vector of UTF-8 strings. The original Tcl object for the string and all substrings refer back to the same vector, with pointers which identify which substring the Tcl object refers to. The shared vector has its own reference count. The UTF-8 strings are unescaped; escaping is only done when converting the internal representation to the string representation; unescaping when converting string to internal.

This makes extracting deescaped symbols easy: just put the UTF-8 string into a Tcl string. Traversing with first, rest, front, and last involve merely adjusting an index and length field without any copying.

Angle marks are used to enclose nested symbol sets.

The SymbolString has reference count and then a start of string pointer for each symbol, with one last pointer pointing after the last string. All symbol strings are stored contiguously in one vector. The SymbolString does not maintain the string length; that is stored in each Tcl object. The first Tcl object has the entire length; substrings derive their length from that object (or another substring object).

Strings may be labeled, the label being on one symbol of the string or the entire string. When the string is copied or substringed, the copy has a copy of the labels. A symbol label is identified by the symbol offset; a substring symbol will need to use its new offset to get the same label. An label is property-value pair of a property (of the string or symbol) name with the property value. If an label value is requested but does not exist, an empty Tcl string is returned.

Labels are preserved in set operations. In a concatenation, each symbol retains its own labels. String labels for the each original string are combined into a list of labels of the concatenated string.

type struct SymbolString—Shared symbol string vector.
int refCount—Number of Tcl objects referring to this symbol string.
chars symbol—Pointer to symbol string; symbol[i] is the first byte address of the ith symbol; its length is symbol[i+1]-symbol[i].
chars (strings)—The strings are stored in the same memory block, after the pointers.

struct SymbolString {
	int refCount;
	chars symbol[1];
};

		
 
section    top
type struct SymbolStringRef—Symbol string reference and size.
SymbolString symbols—The actual symbols.
int first—First symbol in this substring.
int length—Number of symbols in this substring.
bool Reversed—If the string has been reversed.

struct SymbolStringRef {
	SymbolString *symbols;
	int first;
	int length;
	Obj labels;
	bool reversed;
};

		
 
section    top
typedef struct SymbolString SymbolString;
typedef struct SymbolStringRef SymbolStringRef;
		
 
section    top

#define refReverse(ref) ((ref)->reversed = !(ref)->reversed)
#define refIndex(ref,i) ((ref)->first+((ref)->reversed ? (ref)->length-(i)-1 : (i)))
#define origIndex(ref,p) ((ref)->reversed ? (ref)->first + (ref)->length + 1 - (p) : (p) - (ref)->first)
#define symbolString(ref,i) ((ref)->symbols->symbol[refIndex(ref,i)])
#define symbolLength(ref,i) ((ref)->symbols->symbol[refIndex(ref,i)+1]-(ref)->symbols->symbol[refIndex(ref,i)])
#define substringAdvance(ref,o) ( \
		((ref)->reversed ? 0 : ((ref)->first += (o))), \
		(ref)->length -= (o), \
		((ref)->length<0 ? ((ref)->length = 0) : 0), \
		0)
#define substringLength(ref,n) ( \
		((ref)->reversed && (n)<(ref)->length ? ((ref)->first += (ref)->length-(n)) : 0), \
		(ref)->length>(n) ? ((ref)->length = (n)) : 0, \
		((ref)->length<0 ? ((ref)->length = 0) : 0), \
		0)

static int symcmp(Obj x,Obj y) {
	SymbolStringRef *rx; Obj X = extractSymbolString(x,&rx);
	SymbolStringRef *ry; Obj Y = extractSymbolString(y,&ry);
	int i,cc;
	for (i=0,cc=0; cc==0; i++) 
		if (i>=rx->length && i>=ry->length) break;
		else if (i>=rx->length) cc = -1;
		else if (i>=ry->length) cc = 1;
		else {
			chars rxfca = symbolString(rx,i); int rxlen = symbolLength(rx,i);
			chars ryfca = symbolString(ry,i); int rylen = symbolLength(ry,i);
			cc = memcmp(rxfca,ryfca,rxlen<rylen?rxlen:rylen);
			if (cc==0) cc = rxlen-rylen;
		}
	decr(X);
	decr(Y);
	return cc;
}

static int symcmpq(const void *x, const void *y) {
	Obj *X = x,*Y = y; return symcmp(*X,*Y);
}

static int symcmpb(const void *x, const void *y) {
	Obj X = x,*Y = y; return symcmp(X,*Y);
}

		
 
section    top
protocol Create a SymbolString
input int nsymbols—Number of symbols.
input int ncharacters—Total number of characters.
output SymbolStringRef* ref—String reference.
input Obj labels—Labels to copy in or NULL..
		
ref = heap(SymbolStringRef); ref->first = 0; ref->length = nsymbols;  ref->reversed = false;
ref->labels = incr(labels);
if (nsymbols==0) {
	ref->symbols = 0;
}else {
	chars p = allocate(sizeof(SymbolString) + nsymbols*sizeof(chars) + ncharacters+1);
	SymbolString *symbolString = (SymbolString*)p;
	symbolString->refCount = 1;
	symbolString->symbol[0] = p + sizeof(SymbolString) + nsymbols*sizeof(chars);
	ref->symbols = symbolString;
}

		
 
section    top
protocol Append to SymbolString
io SymbolStringRef* ref—Reference to string appended to.
input int i—The ith symbol added.
input chars symbol—Symbol characters.
input int length—Number of characters; if -1, to zero byte.

SymbolString *symbolString = ref->symbols;
if (length<0) length = symbol ? strlen(symbol) : 0;
memcpy(symbolString->symbol[i],symbol,length);
symbolString->symbol[i+1] = symbolString->symbol[i]+length;

		
 
section    top
protocol Duplicate a SymbolString
input SymbolStringRef* ref—Previous reference.
output SymbolStringRef* newref—New reference.

newref = heap(SymbolStringRef);
*newref = *ref;
incr(newref->labels);
if (newref->symbols) newref->symbols->refCount += 1;

		
 
section    top
protocol Free a SymbolString reference
io SymbolStringRef* ref—The reference.

SymbolString *symbolString = ref->symbols;
if (symbolString) {
	symbolString->refCount -= 1;
	if (symbolString->refCount==0) {
		dispose(symbolString);
	}
}
decr(ref->labels);
dispose(ref);

		
 
section    top
protocol Encode a string
io chars s—Beginning of the string to encode; incremented by n.
io int n—Length of the string to encode (to zero byte if -1); decremented to zero.
output Tcl_DString* DS—Encoded string.

if (n<0) n = strlen(s);
for (; n>0; n--,s++) {
	if (isspace(*s) || *s==',' || *s=='|' || *s=='.' || *s=='@'
			|| *s=='{' || *s=='}' || *s==042 || *s=='\\' || *s==':'
			|| n>=3 && s[0]==043 && isxdigit(s[1]) && isxdigit(s[2])
			|| n>=3 && s[0]==043 && s[1]=='N' && s[2]=='S'
	) {
		char T[3];
		T[0] = 043;
		T[1] = "0123456789abcdef"[(*s>>4)&0xF];
		T[2] = "0123456789abcdef"[(*s   )&0xF];
		Tcl_DStringAppend(DS,T,3);
	}else if (*s=='<') {
		int d = 0;
		for (; n>0; n--,s++) {
			if (*s=='<') d++;
			else if (*s=='>' && --d==0) break;
			Tcl_DStringAppend(DS,s,1);
		}
		Tcl_DStringAppend(DS,s,1);
	}else {
		Tcl_DStringAppend(DS,s,1);
	}
}

		
 
section    top
protocol Decode a string
io chars s—Beginning of the string to decode; incremented by n.
io int n—Length of the string to decode (to zero byte if -1); decremented to zero.
output Tcl_DString* DS—Decoded string.

if (n<0) n = strlen(s);
for (; n>0; n--,s++) {
	if (n>=3 && s[0]==043 && isxdigit(s[1]) && isxdigit(s[2])) {
		char T[3],S[1];
		T[0] = s[1]; T[1] = s[2]; T[2] = 0;
		*S = strtol(T,0,16);
		Tcl_DStringAppend(DS,S,1);
		n -= 2; s += 2;
	}else if (*s=='<') {
		int d = 0;
		for (; n>0; n--,s++) {
			if (*s=='<') d++;
			else if (*s=='>' && --d==0) break;
			Tcl_DStringAppend(DS,s,1);
		}
		Tcl_DStringAppend(DS,s,1);
	}else {
		Tcl_DStringAppend(DS,s,1);
	}
}

		
 
section    top

int NE; Obj *PE; bool set = true;
if (N<3) {
	Tcl_WrongNumArgs(intr,2,P,"[string|set] tcl-value"); return TCL_ERROR;
}else if (N>4) {
	Tcl_WrongNumArgs(intr,4,P,": too many parameters"); return TCL_ERROR;
}else if (N==4 && (strbegins("st",Tcl_GetString(P[2])) || strbegins("-st",Tcl_GetString(P[2])))) {
	if (Tcl_ListObjGetElements(intr,P[3],&NE,&PE)!=TCL_OK) return TCL_ERROR;
	set = false;
}else if (N==4 && (strbegins("se",Tcl_GetString(P[2])) || strbegins("-se",Tcl_GetString(P[2])))) {
	if (Tcl_ListObjGetElements(intr,P[3],&NE,&PE)!=TCL_OK) return TCL_ERROR;
	set = true;
}else if (N==4) {
	rprintf(intr,"expected 'string' or 'set': %{y}s",P[2]);
	return TCL_ERROR;
}else  {
	if (Tcl_ListObjGetElements(intr,P[2],&NE,&PE)!=TCL_OK) return TCL_ERROR;
	set = true;
}

		
 
section    top
21. sym encode ::
sym encode
Encode Tcl list values into escaped and delimited strings expected by the symbol string operators. The Tcl list form of a symbol string is a list of the unescaped symbol names; the empty string is an empty list. The Tcl list form of a symbol string set is a list of Tcl symbol string lists; the empty set is the empty list.
If the type string is specified, the Tcl-list-representation is the one deep Tcl list form of a symbol string. If the type set is specified or no type is specified, the Tcl-list-representation is the two deep Tcl list form of a symbol string set.
For example
wyrm::sym encode {{} a {b c} {d e f} {{g h i}} xyz {abc def} {a,b c|d e.f}}
returns
#NS|a|b,c|d,e,f|g#20h#20i|xyz|abc,def|a#2cb,c#7cd,e#2ef
And
wyrm::sym encode set {}
returns
""
And
wyrm::sym encode string {abc def xyz {p q r}}
returns
abc,def,xyz,p#20q#20r
And
wyrm::sym encode string {}
returns
#NS

if (set) {
	Obj *elem = NE ? nheap(NE,Obj) : 0; int i,rc;
	zero(NE,Obj,elem);
	for (i=0,rc=TCL_OK; i<NE && rc==TCL_OK; i++) {
		int n; Obj *p;
		rc = Tcl_ListObjGetElements(intr,PE[i],&n,&p);
		if (rc==TCL_OK) elem[i] = incr(newSymbolStringObj(n,p));
	}
	if (rc==TCL_OK) {
		Tcl_SetObjResult(intr,newSymbolSetObj(0,elem,NE,0,true));
	}else {
		for (i=0; i<NE; i++) decr(elem[i]); dispose(elem);
	}
	return rc;
}else {
	Tcl_SetObjResult(intr,newSymbolStringObj(NE,PE));
	return TCL_OK;
}

		
 
section    top

Obj ss; bool set = true;
if (N<3 || N>4) {
	Tcl_WrongNumArgs(intr,2,P,"[string|set] tcl-value"); return TCL_ERROR;
}else if (N==4 && (strbegins("st",Tcl_GetString(P[2])) || strbegins("-st",Tcl_GetString(P[2])))) {
	ss = P[3]; set = false;
}else if (N==4 && (strbegins("se",Tcl_GetString(P[2])) || strbegins("-se",Tcl_GetString(P[2])))) {
	ss = P[3]; set = true;
}else if (N==4) {
	rprintf(intr,"expected 'string' or 'set': %{y}s",P[2]);
	return TCL_ERROR;
}else  {
	ss = P[2]; set = true;
}

		
 
section    top

if (set) {
	int e; SymbolSet *S = extractSymbolSet(ss); Obj L = incr(Tcl_NewObj());
	for (e=0; e<S->card; e++) {
		SymbolStringRef *ref; Obj string = extractSymbolString(S->elem[e],&ref);
		if (string) {
			int i; Obj E = incr(Tcl_NewObj());
			for (i=0; i<ref->length; i++) {
				Tcl_ListObjAppendElement(0,E,Tcl_NewStringObj(symbolString(ref,i),symbolLength(ref,i)));
			}
			Tcl_ListObjAppendElement(0,L,E); decr(E); decr(string);
		}
	}
	Tcl_SetObjResult(intr,L); decr(L);
}else {
	SymbolStringRef *ref; Obj string = extractSymbolString(ss,&ref);
	if (string) {
		int i; Obj E = incr(Tcl_NewObj());
		for (i=0; i<ref->length; i++) {
			Tcl_ListObjAppendElement(0,E,Tcl_NewStringObj(symbolString(ref,i),symbolLength(ref,i)));
		}
		Tcl_SetObjResult(intr,E); decr(E); decr(string);
	}else {
		return TCL_ERROR;
	}
}
return TCL_OK;

		
 
section    top

static Tcl_ObjType SymbolsType = {
	"wyrm.sym.symbols",
	freeSymbolString,
	dupSymbolString,
	updateFromSymbolString, 
	convertToSymbolString
};
		
 
section    top
proc freeSymbolString—Free the symbol string internal representation.
output obj—Reference to internal symbol string is removed.

static void freeSymbolString(Obj obj) {
	SymbolStringRef *ref = obj->internalRep.otherValuePtr;
	<Free a SymbolString reference>
	obj->typePtr = 0;
	obj->internalRep.otherValuePtr = 0;
}

		
 
section    top
static void freeSymbolString(Obj obj);
		
 
section    top
proc dupSymbolString—Duplicate the symbol string internal representation.
input ss—Original internal symbol string.
output dd—Refers to the same internal symbol string.

static void dupSymbolString(Obj ss,Obj dd) {
	SymbolStringRef *ref = ss->internalRep.otherValuePtr;
	SymbolStringRef *newref;
	<Duplicate a SymbolString>
	dd->typePtr = &SymbolsType;
	dd->internalRep.otherValuePtr = newref;
}

		
 
section    top
static void dupSymbolString(Obj ss,Obj dd);
		
 
section    top
proc updateFromSymbolString—Create the string representation from the internal.
io obj—Internal symbol string converted to string representation.

static void updateFromSymbolString(Obj obj) {
	SymbolStringRef *ref = obj->internalRep.otherValuePtr;
	Tcl_DString DS0,*DS=&DS0;
	Tcl_DStringInit(DS);
	if (ref->length==0) {
		Tcl_DStringAppend(DS,"#NS",3);
	}else {
		int i; chars sep;
		for (i=0,sep=""; i<ref->length; i++,sep=",") {
			chars s = symbolString(ref,i);
			int n = symbolLength(ref,i);
			Tcl_DStringAppend(DS,sep,-1);
			<Symbol string encoding>
		}
	}
	if (ref->labels) {
		Obj L = incr(Tcl_NewObj()); int n; chars s;
		Obj R,*P; int N;
		Tcl_DStringAppend(DS,"@P",2);
		for (wyrm_assocFilter(0,ref->labels,false,0,&R,&N,&P); N>0; N-=2,P+=2) {
			Obj key = P[0];
			Obj data = P[1];
			int n,x; chars s,q;
			s = Tcl_GetStringFromObj(key,&n);
			x = strtol(s,&q,10);
			if (s!=q) {
				Obj t = oprintf("%d%.*s",origIndex(ref,x),n-(q-s),q);
				Tcl_ListObjAppendElement(0,L,t); decr(t);
				Tcl_ListObjAppendElement(0,L,data);
			}else {
				Tcl_ListObjAppendElement(0,L,key);
				Tcl_ListObjAppendElement(0,L,data);
			}
		}
		decr(R);
		s = Tcl_GetStringFromObj(L,&n);
		<Symbol string encoding>
		decr(L);
	}
	obj->length = Tcl_DStringLength(DS);
	obj->bytes = nheap(obj->length+1,char);
	memcpy(obj->bytes,Tcl_DStringValue(DS),obj->length);
	obj->bytes[obj->length] = 0;
	Tcl_DStringFree(DS);
}

		
 
section    top
static void updateFromSymbolString(Obj obj);
		
 
section    top
proc convertToSymbolString—Create the internal symbol string from the string; returns TCL_OK.
output obj—Created internal symbol string.

static Obj stripLabels(chars p,int *l0,char level) {
	int r = *l0,l = 0;
	text:
		if (r==0) return 0;
		else if (*p=='@') {p++,r--; goto at;}
		else {p++; r--; l++; goto text;}
	at:
		if (r==0) return 0;
		else if (!isupper(*p)) {l++; goto text;}
		else if (*p<level) {l++; goto text;}
		else if (*p==level) {
			chars s= p+1; int n = r-1; Obj labels = 0;
			if (n>0) {
				Tcl_DString DS0,*DS=&DS0;
				Tcl_DStringInit(DS);
				<Symbol string decoding>
				labels = Tcl_NewStringObj(Tcl_DStringValue(DS),Tcl_DStringLength(DS));
				Tcl_DStringFree(DS);
			}
			*l0 = l;
			return labels;
		}else return 0;
		
}

static int convertToSymbolString(Intr intr,Obj obj) {
	if (obj && obj->typePtr==&SetType) {
		SymbolSet *set = obj->internalRep.otherValuePtr;
		if (set->card>0 && set->elem[0]->typePtr==&SymbolsType) {
			SymbolStringRef *ref = set->elem[0]->internalRep.otherValuePtr,*newref;
			<Duplicate a SymbolString>
			if (set->card>1) Tcl_InvalidateStringRep(obj);
			obj->typePtr = &SymbolsType;
			obj->internalRep.otherValuePtr = newref;
			return TCL_OK;
		}
	}
	if (obj->typePtr!=&SymbolsType) {
		int msymbols=1,nsymbols=0,*offsymbol = heap(int),i,ncharacters;
		int m; chars s = Tcl_GetStringFromObj(obj,&m);
		SymbolStringRef *ref;
		Obj labels = stripLabels(s,&m,'P');
		chars bar = memchr(s,'|',m);
		Tcl_DString DS0,*DS=&DS0;
		Tcl_DStringInit(DS);
		if (bar) m = bar-s;
		while (m>0) {
			chars comma = memchr(s,',',m);
			int n = comma ? comma-s : m;
			m -= n;
			if (n>0 && (n!=3 || memcmp(s,"#NS",3)!=0)) {
				if (nsymbols+2>=msymbols) {msymbols = 2*(nsymbols+2); offsymbol = reheap(msymbols,int,offsymbol);}
				offsymbol[nsymbols++] = Tcl_DStringLength(DS);
				<Symbol string decoding>
			}
			if (comma) {m--; s++;}
		}
		ncharacters = offsymbol[nsymbols] = Tcl_DStringLength(DS);
		<Create a SymbolString>
		for (i=0; i<nsymbols; i++) {
			chars symbol = Tcl_DStringValue(DS)+offsymbol[i];
			int length = offsymbol[i+1]-offsymbol[i];
			<Append to a SymbolString during creation>
		}
		dispose(offsymbol); Tcl_DStringFree(DS);
		if (obj->typePtr && obj->typePtr->freeIntRepProc)
			obj->typePtr->freeIntRepProc(obj);
		obj->typePtr = &SymbolsType;
		obj->internalRep.otherValuePtr = ref;
	}
	return TCL_OK;
}

		
 
section    top
static int convertToSymbolString(Intr intr,Obj obj);
		
 
section    top
proc newSymbolStringObj—New symbol string.
input nsymbols—Array size.
input symbolobj—Array of symbols.

static Obj newSymbolStringObj(int nsymbols,Obj *symbolobj) {
	Obj obj = heap(Tcl_Obj);
	SymbolStringRef *ref;
	int i,ncharacters = 0;
	Obj labels = 0;
	for (i=0; i<nsymbols; i++) {
		int n; Tcl_GetStringFromObj(symbolobj[i],&n); ncharacters += n;
	}
	<Create a SymbolString>
	for (i=0; i<nsymbols; i++) {
		int length; chars symbol = Tcl_GetStringFromObj(symbolobj[i],&length);
		<Append to a SymbolString during creation>
	}

	obj->refCount = 0; obj->bytes = 0; obj->length = 0;
	obj->typePtr = &SymbolsType;
	obj->internalRep.otherValuePtr = ref;
	return obj;
}

		
 
section    top
static Obj newSymbolStringObj(int nsymbols,Obj *symbolobj);
		
 
section    top
proc newSymbolSubstringObj—Symbol substring.
input ref—Reference to the symbol string.
input o—Offset of the substring from the reference.
input n—Length of the substring.

static Obj newSymbolSubstringObj(SymbolStringRef *ref,int o,int n) {
	Obj obj = heap(Tcl_Obj);
	SymbolStringRef *newref;
	<Duplicate a SymbolString>
	substringAdvance(newref,o);
	substringLength(newref,n);
	obj->refCount = 0; obj->bytes = 0; obj->length = 0;
	obj->typePtr = &SymbolsType;
	obj->internalRep.otherValuePtr = newref;
	return obj;
}

		
 
section    top
static Obj newSymbolSubstringObj(SymbolStringRef *ref,int o,int n);
		
 
section    top
proc newReversedSymbolStringObj—Symbol substring.
input ref—Reference to the symbol string.

static Obj newReversedSymbolStringObj(SymbolStringRef *ref) {
	Obj obj = heap(Tcl_Obj);
	SymbolStringRef *newref;
	<Duplicate a SymbolString>
	refReverse(newref);
	obj->refCount = 0; obj->bytes = 0; obj->length = 0;
	obj->typePtr = &SymbolsType;
	obj->internalRep.otherValuePtr = newref;
	return obj;
}

		
 
section    top
static Obj newReversedSymbolStringObj(SymbolStringRef *ref);
		
 
section    top
proc extractSymbolString—Coerce an object to a symbol string; returns the symbol string object.
Memory. Caller decrements reference count when done.
output Intr intr—Receives error messages; may be NULL.
io Obj obj—Coerced object.
output SymbolStringRef** ref—String reference or NULL if error.

static Obj extractSymbolString(Obj obj,SymbolStringRef **ref) {
	if (!obj) {
		;
	}else if (obj->typePtr==&SymbolsType) {
		incr(obj);
	}else if (obj->typePtr==&SetType) {
		SymbolSet *set = obj->internalRep.otherValuePtr;
		obj = set->card ? incr(set->elem[0]) : 0;		
	}else if (strchr(Tcl_GetString(obj),'|'))  {
		SymbolSet *set = extractSymbolSet(obj);
		obj = set->card ? incr(set->elem[0]) : 0;
	}else {
		incr(obj);
	}
	if (!obj) obj = incr(newSymbolStringObj(0,0));
	convertToSymbolString(0,obj);
	*ref = obj->internalRep.otherValuePtr;
	return obj;
}

		
 
section    top
static Obj extractSymbolString(Obj obj,SymbolStringRef **ref);
		
 
section    top

Obj string; SymbolStringRef *ref;
if (N!=3) {
	Tcl_WrongNumArgs(intr,2,P,"string"); return TCL_ERROR;
}
string = extractSymbolString(P[2],&ref);

		
 
section    top
 
section    top

Tcl_SetObjResult(intr,Tcl_NewBooleanObj(ref->length==0));
decr(string);
return TCL_OK;

		
 
section    top
 
section    top

Tcl_SetObjResult(intr,Tcl_NewIntObj(ref->length));
decr(string);
return TCL_OK;

		
 
section    top
 
section    top

if (ref->length>0) {
	Tcl_SetObjResult(intr,newSymbolSubstringObj(ref,0,1));
	decr(string);
	return TCL_OK;
}else {
	Tcl_SetResult(intr,"empty string",TCL_STATIC);
	decr(string);
	return TCL_ERROR;
}

		
 
section    top
 
section    top


Tcl_SetObjResult(intr,ref->length>0 ? newSymbolSubstringObj(ref,1,ref->length-1) : newSymbolStringObj(0,0));
decr(string);
return TCL_OK;

		
 
section    top
 
section    top

if (ref->length>0) {
	int last = ref->length-1;
	Tcl_SetObjResult(intr,newSymbolSubstringObj(ref,ref->length-1,1));
	decr(string);
	return TCL_OK;
}else {
	Tcl_SetResult(intr,"empty string",TCL_STATIC);
	decr(string);
	return TCL_ERROR;
}

		
 
section    top
 
section    top

Tcl_SetObjResult(intr,ref->length>0 ? newSymbolSubstringObj(ref,0,ref->length-1) : newSymbolStringObj(0,0));
decr(string);
return TCL_OK;

		
 
section    top

Obj string; SymbolStringRef *ref; Obj pos; long num;
if (N