/*--------------------------------------------------------------------*//*:Ignore this sentence.
Copyright (C) 1999, 2001 SIL International. All rights reserved.

Distributable under the terms of either the Common Public License or the
GNU Lesser General Public License, as specified in the LICENSING.txt file.

File: GrcSymTable.cpp
Responsibility: Sharon Correll
Last reviewed: Not yet.

Description:
    Implementation of the parser/compiler symbol table.
-------------------------------------------------------------------------------*//*:End Ignore*/

/***********************************************************************************************
	Include files
***********************************************************************************************/
#include "main.h"

#ifdef _MSC_VER
#pragma hdrstop
#endif
#undef THIS_FILE
DEFINE_THIS_FILE

/***********************************************************************************************
	Forward declarations
***********************************************************************************************/

/***********************************************************************************************
	Local Constants and static variables
***********************************************************************************************/

/***********************************************************************************************
	Methods
***********************************************************************************************/

/*----------------------------------------------------------------------------------------------
    Destructors
----------------------------------------------------------------------------------------------*/
GrcSymbolTable::~GrcSymbolTable()
{
	m_psymParent = NULL;

	for (SymbolTableMap::iterator it = EntriesBegin();
		it != EntriesEnd();
		++it)
	{
		delete it->second; // GetValue();
		//delete it->GetValue();
	}
}


GrcSymbolTableEntry::~GrcSymbolTableEntry()
{
	delete m_psymtblSubTable;
}


/*----------------------------------------------------------------------------------------------
    Add a symbol to the main symbol table in the normal way, assuming there is nothing
	special to do.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::AddSymbol(const GrcStructName & xns, SymbolType symt,
	GrpLineAndFile const& lnf)
{
	Assert(m_cLevel == 0);
	return AddSymbolAux(xns, symt, ksymtInvalid, lnf);
}


/*----------------------------------------------------------------------------------------------
    Add a symbol that is the name of a class to the main symbol table (if it is not already
	there). Also, ensure that it has an GdlGlyphClassDefn as its data.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::AddClassSymbol(const GrcStructName & xns, GrpLineAndFile const& lnf,
	GlyphClassType glfct)
{
	Assert(m_cLevel == 0);
	Assert(xns.NumFields() == 1);

	Symbol psymAdded = AddSymbolAux(xns, ksymtClass, ksymtInvalid, lnf);
	if (!psymAdded->HasData())
	{
		GdlGlyphClassDefn * pglfc;
		GdlGlyphIntersectionClassDefn * pglfci;
		GdlGlyphDifferenceClassDefn * pglfcd;
		switch (glfct)
		{
		case kglfctUnion:
			pglfc = new GdlGlyphClassDefn();
			break;
		// These two options are not used; currently the only way to create an intersection
		// or difference is by converting an existing class as a result of the
		// 'classA &= classB' syntax.
		case kglfctIntersect:
			pglfci = new GdlGlyphIntersectionClassDefn();
			pglfc = dynamic_cast<GdlGlyphClassDefn *>(pglfci);
			break;
		case kglfctDifference:
			pglfcd = new GdlGlyphDifferenceClassDefn();
			pglfc = dynamic_cast<GdlGlyphClassDefn *>(pglfcd);
			break;
		default:
			break;
		}

		pglfc->SetLineAndFile(lnf);
		psymAdded->SetData(pglfc);
	}

	return psymAdded;
}


/*----------------------------------------------------------------------------------------------
    Add a symbol that is the name of a feature to the main symbol table (if it is not already
	there). Also, ensure that it has an GdlFeatureDefn as its data.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::AddFeatureSymbol(const GrcStructName & xns, GrpLineAndFile const& lnf)
{
	Assert(m_cLevel == 0);
	Assert(xns.NumFields() == 1);

	Symbol psymAdded = AddSymbolAux(xns, ksymtFeature, ksymtInvalid, lnf);
	if (!psymAdded->HasData())
	{
		GdlFeatureDefn * pfeat = new GdlFeatureDefn();
		pfeat->SetLineAndFile(lnf);
		psymAdded->SetData(pfeat);
	}
	psymAdded->SetExpType(kexptNumber);

	return psymAdded;
}

/*----------------------------------------------------------------------------------------------
	Add a symbol representing an alternate feature name to the main symbol table.
	The symbol is of the form f_fname_fid (eg, f_smallcaps_smcp).
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::AddFeatureAltSymbol(GdlFeatureDefn * pfeat,
	const GrcStructName & xns, GrpLineAndFile const& lnf)
{
	Assert(m_cLevel == 0);
	Assert(xns.NumFields() == 1);

	Symbol psymAdded = AddSymbolAux(xns, ksymtFeature, ksymtInvalid, lnf);
	Assert(!psymAdded->HasData());
	psymAdded->SetData(pfeat);
	psymAdded->SetExpType(kexptNumber);

	return psymAdded;

}

/*----------------------------------------------------------------------------------------------
    Add a symbol that is the name of a language to the main symbol table (if it is not already
	there). Also, ensure that it has an GdlLanguageDefn as its data.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::AddLanguageSymbol(const GrcStructName & xns, GrpLineAndFile const& lnf)
{
	Assert(m_cLevel == 0);
	Assert(xns.NumFields() == 1);

	Symbol psymAdded = AddSymbolAux(xns, ksymtLanguage, ksymtInvalid, lnf);
	if (!psymAdded->HasData())
	{
		GdlLanguageDefn * plang = new GdlLanguageDefn();
		plang->SetLineAndFile(lnf);
		psymAdded->SetData(plang);
	}
	psymAdded->SetExpType(kexptNumber);

	return psymAdded;
}


/*----------------------------------------------------------------------------------------------
    Add a symbol that is the name of a class's glyph attribute to the main symbol table
	(if it is not already there).
	Ensure that there is a generic version of the glyph attribute in the symbol table
	(minus the class name),	and set a pointer to it.
	If it is one of the standard attributes, set its expression type.
	If is a component glyph attribute, create a corresponding ".ref" slot attribute.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::AddGlyphAttrSymbol(const GrcStructName & xns, GrpLineAndFile const& lnf,
	ExpressionType expt, bool fMetric)
{
	Assert(m_cLevel == 0);
	SymbolType symt = (fMetric) ? ksymtGlyphMetric : ksymtGlyphAttr;
	SymbolType symtOther = (fMetric) ? ksymtInvalid : ksymtNonLeafGlyphAttr;
	Symbol psymAdded = AddSymbolAux(xns, symt, symtOther, lnf);
	psymAdded->AdjustExpTypeIfPossible(expt);

	//	Find or add the generic version of the glyph attribute (without the class name).
	GrcStructName xnsGeneric;
	xns.CopyMinusFirstField(xnsGeneric);	// take off the class name
	Symbol psymGeneric = FindSymbol(xnsGeneric);
	if (psymGeneric)
	{
		if (psymGeneric->m_symt == ksymtGlyphMetric && !fMetric)
		{
			std::string staMsg("Cannot set the value of a glyph metric: ");
			std::string staName = psymGeneric->FullName();
			staMsg.append(staName);
			g_errorList.AddItem(true, 1184, NULL, &lnf, staMsg); // fatal error
			return NULL;
		}
		if (psymGeneric->m_symt == ksymtInvalid)
		{
			//	previously undefined symbol, now we know it is a glyph attribute or metric
			psymGeneric->m_fGeneric = true;
			psymGeneric->m_symt = symt;
		}
		Assert(psymGeneric->m_fGeneric);
		if (!psymGeneric->FitsSymbolType(symt))
		{
			//	Symbol being used as a glyph attribute was previously used as something else.
			Assert(false);
			return psymAdded;
		}

		psymGeneric->AdjustExpTypeIfPossible(expt);
		psymAdded->SetExpType(psymGeneric->ExpType());
	}
	else
	{
		Assert(!fMetric); // all metrics should be defined from the outset

		psymGeneric = AddSymbolAux(xnsGeneric, symt, symtOther, lnf);
		psymGeneric->m_fGeneric = true;
		psymGeneric->AdjustExpTypeIfPossible(expt);
	}

	psymAdded->SetGeneric(psymGeneric);

	//	For component attributes, define the corresponding component.?.ref attribute, and
	//	link up the generic version of the base ligature symbol.
	if (psymGeneric->IsComponentBoxField())
	{
		GrcStructName xnsCompRef(xnsGeneric);
		xnsCompRef.DeleteField(xnsCompRef.NumFields() - 1);
		xnsCompRef.InsertField(xnsCompRef.NumFields(), "reference");
		Symbol psymCompRef = AddSymbolAux(xnsCompRef, ksymtSlotAttr, ksymtSlotAttr, lnf);
		psymCompRef->m_expt = kexptSlotRef;

		Symbol psymBaseLig = psymAdded->BaseLigComponent();
		Assert(psymBaseLig);
		Symbol psymGenericBaseLig = psymGeneric->BaseLigComponent();
		psymGenericBaseLig->m_fGeneric = true;
		psymBaseLig->SetGeneric(psymGenericBaseLig);
	}
		
	return psymAdded;
}


/*----------------------------------------------------------------------------------------------
    Add a symbol that is of the form <class>.component.<X>.... to the main symbol table
	(if it is not already there). CURRENTLY NOT USED
----------------------------------------------------------------------------------------------*/
#if 0
Symbol GrcSymbolTable::AddComponentField(const GrcStructName & xns, GrpLineAndFile const& lnf)
{
	Assert(m_cLevel == 0);
	Assert(xns.FieldEquals(1, "component"));

	SymbolType symt;
	if (xns.FieldEquals(3, "top") ||
			xns.FieldEquals(3, "bottom") ||
			xns.FieldEquals(3, "left") ||
			xns.FieldEquals(3, "right"))
		symt = ksymtGlyphAttr;	// valid component glyph attribute

	else if (xns.FieldEquals(3, "reference"))
		symt = ksymtSlotAttr;	// valid component slot attribute
	else

	{
		//	Invalid use of component attribute.
		Assert(false);
		return NULL;
	}

	Symbol psymAdded = AddGlyphAttrSymbol(xns, lnf, kexptMeas);
	Symbol psymGeneric = psymAdded->Generic();
	Assert(psymGeneric->m_fGeneric);
	ExpressionType exptGeneric = psymGeneric->ExpType();

	if (symt == ksymtGlyphAttr)
	{
		Assert(exptGeneric == kexptUnknown || exptGeneric == kexptMeas);
		psymGeneric->SetExpType(kexptMeas);
		psymAdded->SetExpType(kexptMeas);
	}
	else // symt == ksymtSlotAttr
	{
		Assert(exptGeneric == kexptUnknown || exptGeneric == kexptSlotRef);
		psymGeneric->SetExpType(kexptSlotRef);
		psymAdded->SetExpType(kexptSlotRef);
	}		

	return psymAdded;
}
#endif // 0

/*----------------------------------------------------------------------------------------------
	Add a new unique class-name symbol to the table. Return the new symbol.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::AddAnonymousClassSymbol(GrpLineAndFile const& lnf)
{
	std::string sta = "*GC" + std::to_string(m_csymAnonClass++) + '*';
	Assert(!FindField(sta));
	return AddClassSymbol(sta, lnf);
}

/*----------------------------------------------------------------------------------------------
    Add a symbol to the table if it is not already there. Return an error code if the
	symbol is already there but is of a different type. (Nodes along the way may
	be of different types, but the final node may not.)

	Caller should ensure that if the symbol is already present it has the expected type,
	or raise an error.

	Arguments:
		xns				- structured name of symbol
		symtLeaf		- symbol type to use for leaf node
		symtOther		- symbol type to use for nodes along the way (generally ksymtInvalid
							or ksymtNonLeafGlyphAttr)
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::AddSymbolAux(const GrcStructName & xns,
	SymbolType symtLeaf, SymbolType symtOther, GrpLineAndFile const& lnf)
{
	Symbol psym = NULL;
	GrcSymbolTable * psymtbl = this;

	for (auto i = 0U; i < xns.NumFields(); ++i)
	{
		std::string staField = xns.FieldAt(i);

		if (psymtbl == NULL)	// never true first time through
		{
			psymtbl = new GrcSymbolTable(false);
			psymtbl->MakeSubTableOf(psym);
		}

		psym = psymtbl->FindField(staField);
		if (psym == NULL)
		{
			psym = new GrcSymbolTableEntry(staField,
				((i == xns.NumFields() - 1) ? symtLeaf : symtOther),
				psymtbl);
			SymbolTablePair hmpair;
			hmpair.first = staField;
			hmpair.second = psym;
			psymtbl->m_hmstasymEntries.insert(hmpair);
			//psymtbl->m_hmstasymEntries.Insert(staField, psym);
		}
		psymtbl = psym->m_psymtblSubTable;
	}

	// TODO: if the symbol is of the form <class>.<predefined-glyph-attr>, define it
	// as a glyph attribute.

	if (psym->m_lnf.NotSet())
		psym->m_lnf = lnf;

	if (psym->m_symt == ksymtInvalid)
		psym->SetSymbolType(symtLeaf);

	Assert(psym->m_symt == symtLeaf || psym->m_symt2 == symtLeaf);

	return psym;
}


/*----------------------------------------------------------------------------------------------
    Answer true if the symbol fits the given type.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::FitsSymbolType(SymbolType symt)
{
	//	Exact match
	if (m_symt == symt || m_symt2 == symt)
		return true;

	//	Handle subtypes
	switch (symt)
	{
	case ksymtTable:
		if (FitsSymbolType(ksymtTableRule))
			return true;
		break;

	case ksymtGlyphData:
		if (FitsSymbolType(ksymtGlyphAttr))
			return true;
		if (FitsSymbolType(ksymtGlyphMetric))
			return true;
		break;

	case ksymtGlyphAttr:
		if (FitsSymbolType(ksymtGlyphAttrComp))
			return true;
		break;

	case ksymtInvalid:
		if (FitsSymbolType(ksymtNonLeafGlyphAttr))
			return true;
		break;

	case ksymtSlotAttr:
		if (FitsSymbolType(ksymtSlotAttrPt))
			return true;
		if (FitsSymbolType(ksymtSlotAttrPtOff))
			return true;
		if (FitsSymbolType(ksymtSlotAttrCompRef))
			return true;
		break;

	case ksymtSpecial:
		if (FitsSymbolType(ksymtSpecialAt))
			return true;
		if (FitsSymbolType(ksymtSpecialCaret))
			return true;
		if (FitsSymbolType(ksymtSpecialLb))
			return true;
		if (FitsSymbolType(ksymtSpecialUnderscore))
			return true;
		break;

	case ksymtOperator:
		if (FitsSymbolType(ksymtOpAssign))
			return true;
		break;

	default:
		;	// no subtypes
	}

	return false;
}


/*----------------------------------------------------------------------------------------------
    Return a pointer to the symbol indicated by the field, or NULL if it is not present in
	the table.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::FindField(std::string staField)
{
	SymbolTableMap::iterator hmit = m_hmstasymEntries.find(staField);
	if (hmit == m_hmstasymEntries.end())
		return NULL;
	else
		return hmit->second;

	//Symbol psymRet;
	//if (m_hmstasymEntries.Retrieve(staField, &psymRet))
	//	return psymRet;
	//else
	//	return NULL;
}

/*----------------------------------------------------------------------------------------------
    Return a pointer to the symbol indicated by the structured name, or NULL if the symbol
	is not present.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::FindSymbol(const GrcStructName & xns)
{
	GrcSymbolTable * psymtbl = this;
	Symbol psym = NULL;

	for (auto i = 0U; i < xns.NumFields(); ++i)
	{
		psym = psymtbl->FindField(xns.FieldAt(i));
		if (psym == NULL)
			return NULL;
		psymtbl = psym->m_psymtblSubTable;
		if (i < xns.NumFields() - 1 && psymtbl == NULL)
			return NULL;
	}
	return psym;
}

//	one-field version:
Symbol GrcSymbolTable::FindSymbol(const std::string staName)
{
	return FindField(staName);
}


/*----------------------------------------------------------------------------------------------
    Return the symbol corresponding to the slot attribute name. Return NULL if it is not
	a legal slot attribute. If it is of the form component.???.reference, add it to the
	symbol table.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::FindSlotAttr(const GrcStructName & xns, GrpLineAndFile const& lnf)
{
	Symbol psym = FindSymbol(xns);

	if (!psym && xns.NumFields() == 3 &&
		xns.FieldEquals(0, "component") && xns.FieldEquals(2, "reference"))
	{
		psym = AddSymbolAux(xns, ksymtSlotAttrCompRef, ksymtNonLeafGlyphAttr, lnf);
		psym->SetExpType(kexptSlotRef);
	}

	if (!psym || !psym->FitsSymbolType(ksymtSlotAttr))
		return NULL;
	else
		return psym;
}

/*----------------------------------------------------------------------------------------------
    Return the symbol corresponding to the slot attribute name. Return NULL if it is not
	a legal slot attribute. If it is of the form component.???.reference, add it to the
	symbol table.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::FindFeature(const GrcStructName & xns, GrpLineAndFile const& lnf)
{
	Symbol psym = FindSymbol(xns);
	if (!psym || !psym->FitsSymbolType(ksymtFeature))
		return NULL;
	else
		return psym;
}

/*----------------------------------------------------------------------------------------------
    Replace the data class associated with the symbol with the given class. The 
	existing class should be uninitialized.
----------------------------------------------------------------------------------------------*/
void GrcSymbolTableEntry::ReplaceClassData(GdlGlyphClassDefn * pglfc)
{
	if (m_pData)
	{
		// Empty class is being deleted.
		GdlGlyphClassDefn * pglfcDelete = dynamic_cast<GdlGlyphClassDefn *>(m_pData);
		Assert(pglfcDelete);
		Assert(pglfcDelete->Name() == "");
		pglfcDelete->ComputeMembers();
		Assert(pglfcDelete->GlyphIDCount() == 0);
		delete m_pData;
	}
	SetData(pglfc);
}


/*----------------------------------------------------------------------------------------------
    Answer true if the field at the given index is the given string.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::FieldIs(int i, std::string staField)
{
	Assert(i >= 0);

	if (i > Level())
		return false;

	Symbol psymCurr = this;
	while (psymCurr->Level() > i)
		psymCurr = psymCurr->m_psymtbl->m_psymParent;
	return (psymCurr->m_staFieldName == staField);
}

/*----------------------------------------------------------------------------------------------
    Answer the field at the given index, or an empty string if there are not
	that many fields.
----------------------------------------------------------------------------------------------*/
std::string GrcSymbolTableEntry::FieldAt(int i)
{
	Assert(i >= 0);

	if (i > Level())
		return "";

	Symbol psymCurr = this;
	while (psymCurr->Level() > i)
		psymCurr = psymCurr->m_psymtbl->m_psymParent;
	return psymCurr->m_staFieldName;
}

/*----------------------------------------------------------------------------------------------
    Answer the index  of the (first) field containing the given string, or -1 if it is not
	present.
----------------------------------------------------------------------------------------------*/
int GrcSymbolTableEntry::FieldIndex(std::string sta)
{
	int cRet = -1;
	Symbol psymCurr = this;
	while (psymCurr)
	{
		if (psymCurr->m_staFieldName == sta)
			cRet = psymCurr->Level();
		psymCurr = psymCurr->m_psymtbl->m_psymParent;
	}
	return cRet;
}

/*----------------------------------------------------------------------------------------------
    Answer the number of fields in the symbol.
----------------------------------------------------------------------------------------------*/
int	GrcSymbolTableEntry::FieldCount()
{
	return Level() + 1;
}


/*----------------------------------------------------------------------------------------------
    Answer true if the symbol is the given operator.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::MatchesOp(std::string sta)
{
	if (!FitsSymbolType(ksymtOperator))
		return false;
	return (m_staFieldName == sta);
}


/*----------------------------------------------------------------------------------------------
    Answer true if the symbol is a comparative operator.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsComparativeOp()
{
	if (!FitsSymbolType(ksymtOperator))
		return false;
	return (m_prec == kprecComparative);
}


/*----------------------------------------------------------------------------------------------
    Answer true if the symbol is a bogus slot attribute that is only present in symbol table
	to assist in error checking: advance/shift/kern.gpoint/gpath/xoffset/yoffset.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsBogusSlotAttr()
{
	Assert(FitsSymbolType(ksymtSlotAttr) || FitsSymbolType(ksymtFeature));

	if (FieldCount() != 2)
		return false;

	if (m_staFieldName == "gpoint" || m_staFieldName == "gpath" ||
		m_staFieldName == "xoffset" || m_staFieldName == "yoffset")
	{
		std::string sta = FieldAt(0);
		if (sta == "shift" || sta == "advance" || sta == "kern")
			return true;
	}
	return false;
}


/*----------------------------------------------------------------------------------------------
    Answer true if the symbol is a read-only slot attribute, ie, position, position.x, or
	position.y.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsReadOnlySlotAttr()
{
	Assert(FitsSymbolType(ksymtSlotAttr));

	if (FieldIs(0, "position"))
		return true;
	else if (FieldIs(0, "collision") && FieldIs(1, "fix"))
		return true;
	else
		return false;
}


/*----------------------------------------------------------------------------------------------
    Answer true if the symbol is a write-only slot attribute, ie, kern, kern.x, or
	kern.y.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsWriteOnlySlotAttr()
{
	Assert(FitsSymbolType(ksymtSlotAttr) || FitsSymbolType(ksymtFeature));

	return (FieldIs(0, "kern"));
}


/*----------------------------------------------------------------------------------------------
    Answer true if the symbol is an indexed slot attribute, ie, component.XXX.reference or
	a user-definable slot attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsIndexedSlotAttr()
{
	Assert(FitsSymbolType(ksymtSlotAttr));

	if (FitsSymbolType(ksymtFeature))
		return false;
	else if (FieldIs(0, "component") && LastFieldIs("reference"))
		return true;
	else if (IsUserDefinableSlotAttr())
		return true;
	else
		return false;
}


/*----------------------------------------------------------------------------------------------
    Answer true if the symbol is an indexed glyph attribute, ie, component.XXX....
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsIndexedGlyphAttr()
{
	Assert(FitsSymbolType(ksymtGlyphAttr));

	if (IsGeneric())
		return (FieldIs(0, "component"));
	else
		return Generic()->IsIndexedGlyphAttr();
}


/*----------------------------------------------------------------------------------------------
    Return the full dotted name of the symbol.
----------------------------------------------------------------------------------------------*/
std::string GrcSymbolTableEntry::FullName()
{
	std::string staRet = m_staFieldName;
	GrcSymbolTableEntry * psymCurr = m_psymtbl->m_psymParent;
	while (psymCurr)
	{
		std::string staTmp = psymCurr->m_staFieldName;
		staTmp += ".";
		staTmp += staRet;
		staRet = staTmp;
		psymCurr = psymCurr->m_psymtbl->m_psymParent;
	}
	return staRet;
}


/*----------------------------------------------------------------------------------------------
    Return the full dotted name of the symbol, using abbreviations.
----------------------------------------------------------------------------------------------*/
std::string GrcSymbolTableEntry::FullAbbrev()
{
	std::string staRet = Abbreviation(m_staFieldName);
	GrcSymbolTableEntry * psymCurr = m_psymtbl->m_psymParent;
	while (psymCurr)
	{
		std::string staTmp = Abbreviation(psymCurr->m_staFieldName);
		staTmp += ".";
		staTmp += staRet;
		staRet = staTmp;
		psymCurr = psymCurr->m_psymtbl->m_psymParent;
	}
	return staRet;
}


/*----------------------------------------------------------------------------------------------
    Return the full dotted name of the symbol, using abbreviations. Omit the given string,
	which should be the top level item. For instance, if "attach" is passed, return
	"with.x" instead of "attach.with.x".
----------------------------------------------------------------------------------------------*/
std::string GrcSymbolTableEntry::FullAbbrevOmit(std::string staOmit)
{
	std::string staRet = Abbreviation(m_staFieldName);
	GrcSymbolTableEntry * psymCurr = m_psymtbl->m_psymParent;
	while (psymCurr)
	{
		std::string staTmp = Abbreviation(psymCurr->m_staFieldName);
		if (staTmp == staOmit)
			break;
		staTmp += ".";
		staTmp += staRet;
		staRet = staTmp;
		psymCurr = psymCurr->m_psymtbl->m_psymParent;
	}
	return staRet;
}


/*----------------------------------------------------------------------------------------------
    Return the standard abbreviation for the keyword.
----------------------------------------------------------------------------------------------*/
std::string GrcSymbolTableEntry::Abbreviation(std::string staFieldName)
{
	if (staFieldName == "reference")
		return "ref";
	else if (staFieldName == "boundingbox")
		return "bb";
	else if (staFieldName == "advancewidth")
		return "aw";
	else if (staFieldName == "advanceheight")
		return "ah";
	else if (staFieldName == "leftsidebearing")
		return "lsb";
	else if (staFieldName == "rightsidebearing")
		return "rsb";
	else if (staFieldName == "directionality")
		return "dir";
	else if (staFieldName == "component")
		return "comp";
	else if (staFieldName == "breakweight")
		return "break";
	else
		return staFieldName;
}

/*----------------------------------------------------------------------------------------------
    Fill in the structured name with the name of the symbol.
----------------------------------------------------------------------------------------------*/
void GrcSymbolTableEntry::GetStructuredName(GrcStructName * pxns)
{
	GrcSymbolTableEntry * psymCurr = this;
	while (psymCurr)
	{
		pxns->InsertField(0, psymCurr->m_staFieldName);
		psymCurr = psymCurr->ParentSymbol();
	}
}

/*----------------------------------------------------------------------------------------------
    This symbol is a non-generic glyph attribute or feature setting. Return the symbol
	for the defining class or feature.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTableEntry::BaseDefnForNonGeneric()
{
	Assert(FitsSymbolType(ksymtGlyphAttr) ||
		FitsSymbolType(ksymtFeatSetting) ||
		FitsSymbolType(ksymtInvalid));

	Symbol psymParent = ParentSymbol();
	Assert(psymParent);
	if (psymParent->FitsSymbolType(ksymtClass))
		return psymParent;
	else if (psymParent->FitsSymbolType(ksymtFeature))
		return psymParent;
	else
		return psymParent->BaseDefnForNonGeneric();
}

/*----------------------------------------------------------------------------------------------
    Return the symbol for the defining class, or NULL if this symbol is something other
	than a non-generic glyph attribute.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTableEntry::BaseClassDefn()
{
	if (!FitsSymbolType(ksymtGlyphAttr))
		return NULL;

	Symbol psymParent = ParentSymbol();
	if (!psymParent)
		return NULL;

	if (psymParent->FitsSymbolType(ksymtClass))
		return psymParent;
	else
		return psymParent->BaseClassDefn();
}

/*----------------------------------------------------------------------------------------------
    This symbol is a feature setting value, eg someFeat.settings.opt1.id.
	Return the symbol for the feature setting someFeat.settings.opt1.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTableEntry::BaseFeatSetting()
{
	Assert(FitsSymbolType(ksymtFeatSetting));

	Symbol psymParent = ParentSymbol();
	Assert(psymParent);
	if (psymParent->m_staFieldName == "setting")
		return this;
	else
		return psymParent->BaseFeatSetting();
}

/*----------------------------------------------------------------------------------------------
    This symbol is something like clsABC.component.A.top or component.A.ref.
	Return the symbol corresponding to the component itself: clsABC.component.A.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTableEntry::BaseLigComponent()
{
	Assert(FitsSymbolType(ksymtGlyphAttr) ||
		FitsSymbolType(ksymtSlotAttr) ||
		FitsSymbolType(ksymtNonLeafGlyphAttr));

	Symbol psymParent = ParentSymbol();
	Assert(psymParent);
	if (psymParent->m_staFieldName == "component")
		return this;
	else
		return psymParent->BaseLigComponent();
}

/*----------------------------------------------------------------------------------------------
    This symbol is something like clsABC.somePoint.x. Return the symbol corresponding to the
	point itself: clsABC.somePoint.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTableEntry::BasePoint()
{
	Assert(FitsSymbolType(ksymtGlyphAttr));
	Assert(m_staFieldName == "x" || m_staFieldName == "y" ||
		m_staFieldName == "gpoint" || m_staFieldName == "gpath" ||
		m_staFieldName == "xoffset" || m_staFieldName == "yoffset");

	return ParentSymbol();
}

/*----------------------------------------------------------------------------------------------
    This symbol is something like clsABC.somePoint.x. Return the symbol corresponding to a
	sister field, ie, clsABC.somePoint.y. If such a symbol has not been defined, return NULL.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTableEntry::PointSisterField(std::string staField)
{
	Assert(m_staFieldName == "x" || m_staFieldName == "y" ||
		m_staFieldName == "gpoint" || m_staFieldName == "gpath" ||
		m_staFieldName == "xoffset" || m_staFieldName == "yoffset");

	Symbol psymBase = BasePoint();
	Assert(psymBase);
	Symbol psymRet = psymBase->m_psymtblSubTable->FindField(staField);
	return psymRet;
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form ...component.???.reference.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsComponentRef()
{
	Assert(FitsSymbolType(ksymtSlotAttr) || FitsSymbolType(ksymtFeature));
	Symbol psymParent = ParentSymbol();
	return (FitsSymbolType(ksymtSlotAttr) && m_staFieldName == "reference" &&
		psymParent && psymParent->ParentSymbol() &&
		psymParent->ParentSymbol()->m_staFieldName == "component");
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form ...component.???.top/bottom/left/right
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsComponentBoxField()
{
	Symbol psymParent = ParentSymbol();
	return (psymParent && psymParent->ParentSymbol() &&
		psymParent->ParentSymbol()->m_staFieldName == "component" &&
		(m_staFieldName == "top" || m_staFieldName == "bottom" ||
			m_staFieldName == "left" || m_staFieldName == "right"));
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form ...component.???.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsComponentBase()
{
	return (ParentSymbol() && ParentSymbol()->m_staFieldName == "component");
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form ...???.x or ???.y and there is a sister field
	with the appropriate name. Current only used for the debugger-xml output.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsPointField()
{
	if (m_staFieldName == "x")
		return PointSisterField("y") != NULL;
	else if (m_staFieldName == "y")
		return PointSisterField("x") != NULL;
	else if (m_staFieldName == "xoffset" || m_staFieldName == "yoffset")
		return (PointSisterField("x") != NULL && PointSisterField("y") != NULL)
			|| (PointSisterField("gpoint") != NULL);
	else if (m_staFieldName == "gpoint")
		return true;
	else
		return false;
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form "attach.to"
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsAttachTo()
{
	Assert(FitsSymbolType(ksymtSlotAttr) || FitsSymbolType(ksymtFeature));
	Symbol psymParent = ParentSymbol();
	return (FitsSymbolType(ksymtSlotAttr) && psymParent &&
		psymParent->m_staFieldName == "attach" && m_staFieldName == "to");
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form "attach.at.???"
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsAttachAtField()
{
	Assert(FitsSymbolType(ksymtSlotAttr) || FitsSymbolType(ksymtFeature));
	Symbol psymParent = ParentSymbol();
	return (FitsSymbolType(ksymtSlotAttr)
		&& psymParent && psymParent->ParentSymbol()
		&& psymParent->ParentSymbol()->m_staFieldName == "attach"
		&& psymParent->m_staFieldName == "at");
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form "attach.with.???"
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsAttachWithField()
{
	Assert(FitsSymbolType(ksymtSlotAttr) || FitsSymbolType(ksymtFeature));
	Symbol psymParent = ParentSymbol();
	return (FitsSymbolType(ksymtSlotAttr)
		&& psymParent && psymParent->ParentSymbol()
		&& psymParent->ParentSymbol()->m_staFieldName == "attach"
		&& psymParent->m_staFieldName == "with");
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form "attach.at/with.x"
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsAttachXField()
{
	Assert(FitsSymbolType(ksymtSlotAttr) || FitsSymbolType(ksymtFeature));
	Symbol psymParent = ParentSymbol();
	return (FitsSymbolType(ksymtSlotAttr)
		&& (psymParent && psymParent->ParentSymbol())
		&& (psymParent->ParentSymbol()->m_staFieldName == "attach")
		&& ((psymParent->m_staFieldName == "with") || (psymParent->m_staFieldName == "at"))
		&& m_staFieldName == "x");
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form "attach.at/with.xoffset/yoffset"
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsAttachOffsetField()
{
	Assert(FitsSymbolType(ksymtSlotAttr) || FitsSymbolType(ksymtFeature));
	// Symbol psymParent = ParentSymbol();
	return ((IsAttachWithField() || IsAttachAtField())
		&& (m_staFieldName == "xoffset" || m_staFieldName == "yoffset"));
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is an attachment attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsAttachment()
{
	if (m_staFieldName == "attach")
		return true;
	Symbol psymParent = ParentSymbol();
	if (!psymParent)
		return false;
	return (psymParent->IsAttachment());
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is an movement attribute: shift, advance, kern.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsMovement()
{
	if (m_staFieldName == "shift")
		return true;
	if (m_staFieldName == "kern")
		return true;
	if (m_staFieldName == "advance")
		return true;
	Symbol psymParent = ParentSymbol();
	if (!psymParent)
		return false;
	return (psymParent->IsMovement());
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is a justification-related attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::DoesJustification()
{
	if (m_staFieldName == "justify")
		return true;
	Symbol psymParent = ParentSymbol();
	if (!psymParent)
		return false;
	return (psymParent->DoesJustification());
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is a justification-related attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsMeasureAttr()
{
	if (m_staFieldName == "measure")
		return true;
	Symbol psymParent = ParentSymbol();
	if (!psymParent)
		return false;
	return (psymParent->IsMeasureAttr());
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is a mirroring-related attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsMirrorAttr()
{
	if (m_staFieldName == "mirror")
		return true;
	Symbol psymParent = ParentSymbol();
	if (!psymParent)
		return false;
	return (psymParent->IsMirrorAttr());
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is a collision-related attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsCollisionAttr()
{
	if (m_staFieldName == "collision")
		return true;
	Symbol psymParent = ParentSymbol();
	if (!psymParent)
		return false;
	return (psymParent->IsCollisionAttr());
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is a sequence-related attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsSequenceAttr()
{
	if (m_staFieldName == "sequence")
		return true;
	Symbol psymParent = ParentSymbol();
	if (!psymParent)
		return false;
	return (psymParent->IsSequenceAttr());
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is a user-definable slot attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsUserDefinableSlotAttr()
{
	if (m_staFieldName[0] == 'u' && m_staFieldName[1] == 's' &&
		m_staFieldName[2] == 'e' && m_staFieldName[3] == 'r')
	{
		return true;
	}
	else
		return false;
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is a pseudo-slot attribute, used only by the compiler for
	optimization purposes.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsPseudoSlotAttr()
{
	if (m_staFieldName == "passKeySlot")
		return true;
	else
		return false;
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is the pseudo passKeySlot attribute.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsPassKeySlot()
{
	if (m_staFieldName == "passKeySlot")
		return true;
	else
		return false;
}

/*----------------------------------------------------------------------------------------------
    Return true if the symbol is of the form "attach.at/with.gpoint"
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::IsIgnorableOffsetAttr()
{
	Assert(FitsSymbolType(ksymtGlyphAttr));
	//Symbol psymParent = ParentSymbol();
	return (!IsSequenceAttr()
		&& (m_staFieldName == "gpoint" || m_staFieldName == "xoffset"
				|| m_staFieldName == "yoffset"));
}

/*----------------------------------------------------------------------------------------------
	If the symbol is a feature ID represents one of the alternate IDs
	for a feature, return the index of that alternate in the feature's list.
	Such a name looks like featureName__idxx.
	Otherwise return -1;

	Must match CreateFeatAltIDSymbol.
----------------------------------------------------------------------------------------------*/
int GrcSymbolTableEntry::FeatAltIDIndex() {
	Assert(FitsSymbolType(ksymtFeature));

	GdlFeatureDefn * pfeat = this->FeatureDefnData();

	if (!pfeat)
		return -1;
	if (m_staFieldName == pfeat->Name())
		return 0;
	if (m_staFieldName.substr(m_staFieldName.length() - 6, 2) != "__")
		return -1;

	//char ch = m_staFieldName[featID.length() - 5];
	//if (ch != '_')
	//	return -1;
	////ch = m_staFieldName[featID.length() - 6];
	//if (m_staFieldName[featID.length() - 6] != '_')
	//	return -1;

	std::string staAltID = m_staFieldName.substr(m_staFieldName.length() - 4, 4);  // last four chars
	GdlStringExpression expString(staAltID.c_str(), 0);
	unsigned int nID;
	expString.ResolveToFeatureID(&nID);

	std::vector<unsigned int> vnIDs;
	pfeat->AltIDs(vnIDs);
	for (auto i = 0U; i < pfeat->NumAltIDs(); ++i)
	{
		if (vnIDs[i] == nID)
			return i;
	}
	return -1;
}

/*----------------------------------------------------------------------------------------------
	Create a feature symbol that maps to an alternate IDs. It looks like featureName__idxx.

	Must match FeatAltIDIndex.
----------------------------------------------------------------------------------------------*/
void GrcSymbolTableEntry::CreateFeatAltIDSymbol(GrcSymbolTable * psymtbl, GdlFeatureDefn * pfeat,
	GdlStringExpression * pexpString)
{
	std::string staValue = pexpString->StringValue();
	GrpLineAndFile lnf = pexpString->LineAndFile();
	std::string staNewSymbol;
	staNewSymbol.append(pfeat->Name());
	staNewSymbol.append("__");
	staNewSymbol.append(staValue);
	psymtbl->AddFeatureAltSymbol(pfeat, staNewSymbol, lnf);
}

/*----------------------------------------------------------------------------------------------
    Return the number corresponding to the index of the user-definable slot attribute,
	or -1 if it is something invalid (does not parse to a number).
----------------------------------------------------------------------------------------------*/
int GrcSymbolTableEntry::UserDefinableSlotAttrIndex()
{
	Assert(IsUserDefinableSlotAttr());
	int nRet = 0;
	for (size_t ich = 4; ich < m_staFieldName.length(); ich++)
	{
		char ch = m_staFieldName[ich];
		if (ch < '0')
			return -1;
		if (ch > '9')
			return -1;
		nRet = (nRet * 10) + (ch - '0');
	}
	return nRet - 1;	// 0-based
}

/*----------------------------------------------------------------------------------------------
    For slot attributes that use points (eg, attach.at, shift), or for glyph attributes
	that define a point (eg, udap) return the sub-symbol that appends the given point field,
	or NULL if such a field does not exist.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTableEntry::SubField(std::string sta)
{
	Assert(FitsSymbolType(ksymtSlotAttrPt) || FitsSymbolType(ksymtSlotAttrPtOff)
		|| FitsSymbolType(ksymtGlyphAttr) || FitsSymbolType(ksymtNonLeafGlyphAttr)) ;
	Assert(m_psymtblSubTable);
	return m_psymtblSubTable->FindField(sta);
}

/*----------------------------------------------------------------------------------------------
    Return the class that is stored as data, or NULL.
----------------------------------------------------------------------------------------------*/
GdlGlyphClassDefn * GrcSymbolTableEntry::GlyphClassDefnData()
{
	if (!HasData())
		return NULL;

	GdlGlyphClassDefn * pglfc = dynamic_cast<GdlGlyphClassDefn *>(Data());
	return pglfc;
}

/*----------------------------------------------------------------------------------------------
    Return the feature that is stored as data, or NULL.
----------------------------------------------------------------------------------------------*/
GdlFeatureDefn * GrcSymbolTableEntry::FeatureDefnData()
{
	if (!HasData())
		return NULL;

	GdlFeatureDefn * pfeat = dynamic_cast<GdlFeatureDefn *>(Data());
	return pfeat;
}

/*----------------------------------------------------------------------------------------------
    Return the language-map that is stored as data, or NULL.
----------------------------------------------------------------------------------------------*/
GdlLanguageDefn * GrcSymbolTableEntry::LanguageDefnData()
{
	if (!HasData())
		return NULL;

	GdlLanguageDefn * plang = dynamic_cast<GdlLanguageDefn *>(Data());
	return plang;
}

/*----------------------------------------------------------------------------------------------
    Return the level of the justification symbol. Return -2 if this is not such a symbol;
	-1 if this has no level associated (eg, justify.stretch).
----------------------------------------------------------------------------------------------*/
int GrcSymbolTableEntry::JustificationLevel()
{
	if (!DoesJustification())
		return -2;

	std::string sta;
	int nLevel = 0;
	do {
		sta = FieldAt(nLevel);
		nLevel++;
	} while (sta != "justify");
	sta = FieldAt(nLevel);

	if (sta == "0")
		return 0;
	else if (sta == "1")
		return 1;
	else if (sta == "2")
		return 2;
	else if (sta == "3")
		return 3;
	else if (sta == "stretch" || sta == "stretchHW" || sta == "shrink" || sta == "step"
		|| sta == "weight" || sta == "width")
	{
		return -1; // no level specified
	}
	else
		return 4; // some invalid level
}

/*----------------------------------------------------------------------------------------------
    Adjust the expression type to be something more specific, if possible. If the new
	expression type does not fit within the constraints of the old, return false.
----------------------------------------------------------------------------------------------*/
bool GrcSymbolTableEntry::AdjustExpTypeIfPossible(ExpressionType expt)
{
	if (m_expt == kexptUnknown)
	{
		m_expt = expt;
		return true;
	}
	if (m_expt == expt)
	{
		return true;
	}
	if (m_expt == kexptZero && (expt == kexptNumber || expt == kexptMeas || expt == kexptBoolean))
	{
		m_expt = expt;
		return true;
	}
	if (m_expt == kexptOne && (expt == kexptNumber || expt == kexptBoolean))
	{
		m_expt = expt;
		return true;
	}
	return false;
}

/*----------------------------------------------------------------------------------------------
    For slot attributes, return the corresponding engine code operator.
----------------------------------------------------------------------------------------------*/
int GrcSymbolTableEntry::SlotAttrEngineCodeOp()
{
	Assert(FitsSymbolType(ksymtSlotAttr)) ;

	std::string staField0 = FieldAt(0);
	std::string staField1 = FieldAt(1);
	std::string staField2 = FieldAt(2);
	std::string staField3 = FieldAt(3);

	if (staField0 == "advance")
	{
		if (staField1 == "x")
			return kslatAdvX;
		else if (staField1 == "y")
			return kslatAdvY;
		else
		{
			Assert(false);
		}
	}
	else if (staField0 == "attach")
	{
		if (staField1 == "to")
			return kslatAttTo;

		else if (staField1 == "at")
		{
			if (staField2 == "x")
				return kslatAttAtX;
			else if (staField2 == "y")
				return kslatAttAtY;
			else if (staField2 == "gpoint")
				return kslatAttAtGpt;
			else if (staField2 == "xoffset")
				return kslatAttAtXoff;
			else if (staField2 == "yoffset")
				return kslatAttAtYoff;
			else
			{
				Assert(false);
			}
		}
		else if (staField1 == "with")
		{
			if (staField2 == "x")
				return kslatAttWithX;
			else if (staField2 == "y")
				return kslatAttWithY;
			else if (staField2 == "gpoint")
				return kslatAttWithGpt;
			else if (staField2 == "xoffset")
				return kslatAttWithXoff;
			else if (staField2 == "yoffset")
				return kslatAttWithYoff;
			else
			{
				Assert(false);
			}
		}

		else if (staField1 == "level")
			return kslatAttLevel;

		else
		{
			Assert(false);
		}
	}
	else if (staField0 == "breakweight")
		return kslatBreak;

	else if (staField0 == "component")
	{
		Assert(staField2 == "reference");
		return kslatCompRef;
	}

	else if (staField0 == "directionality")
		return kslatDir;

	else if (staField0 == "insert")
		return kslatInsert;

	else if (staField0 == "position")
	{
		if (staField1 == "x")
			return kslatPosX;
		else if (staField1 == "y")
			return kslatPosY;
		else
		{
			Assert(false);
		}
	}

	else if (staField0 == "shift")
	{
		if (staField1 == "x")
			return kslatShiftX;
		else if (staField1 == "y")
			return kslatShiftY;
		else
		{
			Assert(false);
		}
	}

	else if (staField0 == "measure")
	{
		if (staField1 == "startofline")
			return kslatMeasureSol;
		else if (staField1 == "endofline")
			return kslatMeasureEol;
		else
		{
			Assert(false);
		}
	}

	else if (staField0 == "justify")	// TODO: handle all the levels
	{
		if (staField1 == "stretch")
			return kslatJ0Stretch;
		else if (staField1 == "shrink")
			return kslatJ0Shrink;
		else if (staField1 == "step")
			return kslatJ0Step;
		else if (staField1 == "weight")
			return kslatJ0Weight;
		else if (staField1 == "width")
			return kslatJ0Width;
		else if (staField1 == "0" || staField1 == "1" || staField1 == "2" || staField1 == "3")
		{
			int slatStretch = kslatJ0Stretch;
			if (staField1 == "0")
				slatStretch = kslatJ0Stretch;
			else if (staField1 == "1")
				slatStretch = kslatJ1Stretch;
			else if (staField1 == "2")
				slatStretch = kslatJ2Stretch;
			else if (staField1 == "3")
				slatStretch = kslatJ3Stretch;
			else
				Assert(false);

			if (staField2 == "stretch")
				return slatStretch;
			else if (staField2 == "shrink")
				return slatStretch + (kslatJ0Shrink - kslatJ0Stretch);
			else if (staField2 == "step")
				return slatStretch + (kslatJ0Step - kslatJ0Stretch);
			else if (staField2 == "weight")
				return slatStretch + (kslatJ0Weight - kslatJ0Stretch);
			else if (staField2 == "width")
				return slatStretch + (kslatJ0Width - kslatJ0Stretch);
			else
			{
				Assert(false);
			}
		}
		else
		{
			Assert(false);
		}
	}

	else if (staField0 == "segsplit")
		return kslatSegSplit;

	else if (IsUserDefinableSlotAttr())
	{
		return kslatUserDefn;
	}

	else if (staField0[0] == 'u' && staField0[1] == 's' && 
		staField0[2] == 'e' && staField0[3] == 'r')
	{
		Assert(false);
		return kslatUserDefn;
	}

	else if (staField0 == "collision")
	{
		if (staField1 == "flags")
			return kslatColFlags;
		else if (staField1 == "status")
			return kslatColFlags;	// engine returns both of these ORed together
		else if (staField1 == "margin")
			return kslatColMargin;
		else if (staField1 == "marginweight")
			return kslatColMarginWt;

		//else if (staField1 == "order")
		//{
		//	if (staField2 == "class")
		//		return kslatColOrderClass;
		//	else if (staField2 == "enforce")
		//		return kslatColOrderEnforce;
		//	else
		//	{
		//		Assert(false);
		//	}
		//}
		else if (staField1 == "min")
		{
			if (staField2 == "x")
				return kslatColMinX;
			else if (staField2 == "y")
				return kslatColMinY;
			else
			{
				Assert(false);
			}
		}
		else if (staField1 == "max")
		{
			if (staField2 == "x")
				return kslatColMaxX;
			else if (staField2 == "y")
				return kslatColMaxY;
			else
			{
				Assert(false);
			}
		}
		else if (staField1 == "exclude")
		{
			if (staField2 == "glyph")
				return kslatColExclGlyph;
			else if (staField2 == "offset")
			{
				if (staField3 == "x")
					return kslatColExclOffX;
				else if (staField3 == "y")
					return kslatColExclOffY;
				else
				{
					Assert(false);
				}
			}
			else
			{
				Assert(false);
			}
		}
		else if (staField1 == "fix")
		{
			if (staField2 == "x")
				return kslatColFixX;
			else if (staField2 == "y")
				return kslatColFixY;
			else
			{
				Assert(false);
			}
		}
		else
		{
			Assert(false);
		}
	}
	else if (staField0 == "sequence")
	{
		if (staField1 == "class")
			return kslatSeqClass;
		else if (staField1 == "proxClass")
			return kslatSeqProxClass;
		else if (staField1 == "order")
			return kslatSeqOrder;
		else if (staField1 == "above")
		{
			if (staField2 == "xoffset")
				return kslatSeqAboveXoff;
			else if (staField2 == "weight")
				return kslatSeqAboveWt;
			else
			{
				Assert(false);
			}
		}
		else if (staField1 == "below")
		{
			if (staField2 == "xlimit")
				return kslatSeqBelowXlim;
			else if (staField2 == "weight")
				return kslatSeqBelowWt;
			else
			{
				Assert(false);
			}
		}
		else if (staField1 == "valign")
		{
			if (staField2 == "height")
				return kslatSeqValignHt;
			else if (staField2 == "weight")
				return kslatSeqValignWt;
			else
			{
				Assert(false);
			}
		}
		else
		{
			Assert(false);
		}
	}

	else if (IsPseudoSlotAttr())
	{
		return kslatBogus;
	}
	else
	{
		Assert(false);
	}

	return -1;
}


/*----------------------------------------------------------------------------------------------
    For glyph metrics, return the corresponding engine code operator.
----------------------------------------------------------------------------------------------*/
int GrcSymbolTableEntry::GlyphMetricEngineCodeOp()
{
	if (!m_fGeneric && m_psymGeneric)
		return m_psymGeneric->GlyphMetricEngineCodeOp();

	Assert(FitsSymbolType(ksymtGlyphMetric)) ;

	std::string staField0 = FieldAt(0);
	std::string staField1 = FieldAt(1);
	std::string staField2 = FieldAt(2);

	if (staField0 == "leftsidebearing")
	{
		return kgmetLsb;
	}
	else if (staField0 == "rightsidebearing")
	{
		return kgmetRsb;
	}
	else if (staField0 == "boundingbox")
	{
		if (staField1 == "top")
			return kgmetBbTop;
		else if (staField1 == "bottom")
			return kgmetBbBottom;
		else if (staField1 == "left")
			return kgmetBbLeft;
		else if (staField1 == "right")
			return kgmetBbRight;
		else if (staField1 == "width")
			return kgmetBbWidth;
		else if (staField1 == "height")
			return kgmetBbHeight;
		else
		{
			Assert(false);
		}
	}
	else if (staField0 == "advancewidth")
	{
		return kgmetAdvWidth;
	}
	else if (staField0 == "advanceheight")
	{
		return kgmetAdvHeight;
	}
//	else if (staField0 == "advance")
//	{
//		if (staField1 == "width")
//			return kgmetAdvWidth;
//		else if (staField1 == "height")
//			return kgmetAdvHeight;
//		else
//			Assert(false);
//	}
	else if (staField0 == "ascent")
	{
		return kgmetAscent;
	}
	else if (staField0 == "descent")
	{
		return kgmetDescent;
	}
	else
	{
		Assert(false);
    }
	return -1;
}


/*----------------------------------------------------------------------------------------------
	Pre-define a system-defined symbol.
----------------------------------------------------------------------------------------------*/
Symbol GrcSymbolTable::PreDefineSymbol(const GrcStructName& xns, SymbolType symt,
	ExpressionType expt, OpPrec prec)
{
	Symbol psym = AddSymbolAux(xns, symt, ksymtInvalid, GrpLineAndFile());

	psym->m_expt = expt;
	psym->m_fUserDefined = false;
	psym->m_prec = prec;

	return psym;
}


/*----------------------------------------------------------------------------------------------
	Symbol table initialization
----------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitWithPreDefined()
{
	InitGlobals();
	InitDirectives();
	InitFeatureSettings();
	InitGlyphMetrics();
	InitOperators();
	InitSlotAttrs();
	InitGlyphAttrs();
	InitSpecial();
	InitTableTypes();
	InitUnits();
	InitProcStates();

	PreDefineSymbol(GrcStructName(GdlGlyphClassDefn::Undefined()), ksymtClass);
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitGlobals()
{
	SymbolType kst = ksymtGlobal;

	PreDefineSymbol(GrcStructName("AutoPseudo"),			kst, kexptBoolean);
	PreDefineSymbol(GrcStructName("Bidi"),					kst, kexptBoolean);
	PreDefineSymbol(GrcStructName("ExtraAscent"),			kst, kexptMeas);
	PreDefineSymbol(GrcStructName("ExtraDescent"),			kst, kexptMeas);
	PreDefineSymbol(GrcStructName("ScriptDirection"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("ScriptDirections"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("ScriptTags"),			kst, kexptString);
	PreDefineSymbol(GrcStructName("ScriptTag"),				kst, kexptString);
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitDirectives()
{
	SymbolType kst = ksymtDirective;

	PreDefineSymbol(GrcStructName("AttributeOverride"),		kst, kexptBoolean);
	PreDefineSymbol(GrcStructName("CodePage"),				kst, kexptNumber);
	PreDefineSymbol(GrcStructName("MaxBackup"),				kst, kexptNumber);
	PreDefineSymbol(GrcStructName("MaxRuleLoop"),			kst, kexptNumber);
	PreDefineSymbol(GrcStructName("MUnits"),				kst, kexptMeas);
	PreDefineSymbol(GrcStructName("PointRadius"),			kst, kexptMeas);
	PreDefineSymbol(GrcStructName("CollisionFix"),			kst, kexptBoolean);
	PreDefineSymbol(GrcStructName("AutoKern"),				kst, kexptNumber);
	PreDefineSymbol(GrcStructName("CollisionThreshold"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("Direction"),				kst, kexptNumber);
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitFeatureSettings()
{
	SymbolType kst = ksymtFeatSetting;

	PreDefineSymbol(GrcStructName("id"),				kst, kexptNumber);
	PreDefineSymbol(GrcStructName("default"),			kst, kexptNumber);
	PreDefineSymbol(GrcStructName("settings"),			kst, kexptUnknown);
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitGlyphMetrics()
{
	SymbolType kst = ksymtGlyphMetric;

	Symbol psym;
	psym = PreDefineSymbol(GrcStructName("boundingbox", "top"),	kst, kexptMeas);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("boundingbox", "bottom"),	kst, kexptMeas);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("boundingbox", "left"),	kst, kexptMeas);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("boundingbox", "right"),	kst, kexptMeas);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("boundingbox", "height"),	kst, kexptMeas);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("boundingbox", "width"),	kst, kexptMeas);
	psym->m_fGeneric = true;

	psym = PreDefineSymbol(GrcStructName("advanceheight"),		kst, kexptMeas);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("advancewidth"),		kst, kexptMeas);
	psym->m_fGeneric = true;

	psym = PreDefineSymbol(GrcStructName("leftsidebearing"),	kst, kexptMeas);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("rightsidebearing"),	kst, kexptMeas);	// = munits - lsb
	psym->m_fGeneric = true;

	psym = PreDefineSymbol(GrcStructName("ascent"),	kst, kexptMeas);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("descent"),	kst, kexptMeas);	// = font height - ascent
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("munits"),	kst, kexptNumber);
	psym->m_fGeneric = true;
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitOperators()
{
	SymbolType kst = ksymtOperator;
	ExpressionType kexpt = kexptUnknown;

	OpPrec kprec = GrcSymbolTableEntry::kprecFunctional;
	PreDefineSymbol(GrcStructName("min"),	kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("max"),	kst, kexpt, kprec);

	kprec = GrcSymbolTableEntry::kprecAssignment;
	PreDefineSymbol(GrcStructName("="),		ksymtOpAssign, kexpt, kprec);
	PreDefineSymbol(GrcStructName("+="),	ksymtOpAssign, kexpt, kprec);
	PreDefineSymbol(GrcStructName("-="),	ksymtOpAssign, kexpt, kprec);
	PreDefineSymbol(GrcStructName("*="),	ksymtOpAssign, kexpt, kprec);
	PreDefineSymbol(GrcStructName("/="),	ksymtOpAssign, kexpt, kprec);
	//PreDefineSymbol(GrcStructName("&="),	ksymtOpAssign, kexpt, kprec); -- not implemented
	//PreDefineSymbol(GrcStructName("|="),	ksymtOpAssign, kexpt, kprec); -- not implemented

	kprec = GrcSymbolTableEntry::kprecConditional;
	PreDefineSymbol(GrcStructName("?"),		kst, kexpt, kprec);

	kprec = GrcSymbolTableEntry::kprecLogical;
	PreDefineSymbol(GrcStructName("||"),	kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("&&"),	kst, kexpt, kprec);

	kprec = GrcSymbolTableEntry::kprecComparative;
	PreDefineSymbol(GrcStructName("=="),	kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("!="),	kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("<"),		kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("<="),	kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName(">"),		kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName(">="),	kst, kexpt, kprec);

	kprec = GrcSymbolTableEntry::kprecAdditive;
	PreDefineSymbol(GrcStructName("+"),		kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("-"),		kst, kexpt, kprec);

	kprec = GrcSymbolTableEntry::kprecMultiplicative;
	PreDefineSymbol(GrcStructName("*"),		kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("/"),		kst, kexpt, kprec);

	kprec = GrcSymbolTableEntry::kprecBitwise;
	// Strictly speaking these two operators don't have the same precedence,
	// but it doesn't matter since apparently we don't use the precedence value
	// for anything.
	PreDefineSymbol(GrcStructName("&"),		kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("|"),		kst, kexpt, kprec);

	kprec = GrcSymbolTableEntry::kprecNegational;
	PreDefineSymbol(GrcStructName("!"),		kst, kexpt, kprec);
	PreDefineSymbol(GrcStructName("~"),		kst, kexpt, kprec);
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitSlotAttrs()
{
	SymbolType kst = ksymtSlotAttr;
	SymbolType kstPt = ksymtSlotAttrPt;
	SymbolType kstPtO = ksymtSlotAttrPtOff;

	PreDefineSymbol(GrcStructName("attach", "to"),				kst, kexptSlotRef);
	PreDefineSymbol(GrcStructName("attach", "level"),			kst, kexptNumber);

	PreDefineSymbol(GrcStructName("attach", "at"),				kstPtO, kexptPoint);
	PreDefineSymbol(GrcStructName("attach", "at", "x"),			kst, kexptMeas);
	PreDefineSymbol(GrcStructName("attach", "at", "y"),			kst, kexptMeas);
	PreDefineSymbol(GrcStructName("attach", "at", "gpath"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("attach", "at", "gpoint"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("attach", "at", "xoffset"),	kst, kexptMeas);
	PreDefineSymbol(GrcStructName("attach", "at", "yoffset"),	kst, kexptMeas);

	PreDefineSymbol(GrcStructName("attach", "with"),			kstPtO, kexptPoint);
	PreDefineSymbol(GrcStructName("attach", "with", "x"),		kst, kexptMeas);
	PreDefineSymbol(GrcStructName("attach", "with", "y"),		kst, kexptMeas);
	PreDefineSymbol(GrcStructName("attach", "with", "gpath"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("attach", "with", "gpoint"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("attach", "with", "xoffset"),	kst, kexptMeas);
	PreDefineSymbol(GrcStructName("attach", "with", "yoffset"),	kst, kexptMeas);

	PreDefineSymbol(GrcStructName("breakweight"),	kst, kexptNumber);

	PreDefineSymbol(GrcStructName("component"),		kst);
	//	Specific component.?.reference attributes are added as component glyph attributes
	//	are defined, or as encountered.

	PreDefineSymbol(GrcStructName("directionality"),kst, kexptNumber);

	PreDefineSymbol(GrcStructName("user1"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user2"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user3"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user4"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user5"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user6"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user7"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user8"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user9"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user10"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user11"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user12"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user13"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user14"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user15"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("user16"),	kst, kexptNumber);

	PreDefineSymbol(GrcStructName("insert"),		kst, kexptBoolean);

	PreDefineSymbol(GrcStructName("advance"),		kstPtO,	kexptPoint);
	PreDefineSymbol(GrcStructName("advance", "x"),	kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("advance", "y"),	kst,	kexptMeas);
	//	bogus attributes for error detection:
	PreDefineSymbol(GrcStructName("advance", "gpath"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("advance", "gpoint"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("advance", "xoffset"),	kst, kexptMeas);
	PreDefineSymbol(GrcStructName("advance", "yoffset"),	kst, kexptMeas);

	PreDefineSymbol(GrcStructName("kern"),				kstPtO,	kexptPoint);
	PreDefineSymbol(GrcStructName("kern", "x"),			kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("kern", "y"),			kst,	kexptMeas);
	//	bogus attributes for error detection:
	PreDefineSymbol(GrcStructName("kern", "gpath"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("kern", "gpoint"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("kern", "xoffset"),	kst, kexptMeas);
	PreDefineSymbol(GrcStructName("kern", "yoffset"),	kst, kexptMeas);

	PreDefineSymbol(GrcStructName("position"),			kstPtO,	kexptPoint);
	PreDefineSymbol(GrcStructName("position", "x"),		kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("position", "y"),		kst,	kexptMeas);

	PreDefineSymbol(GrcStructName("shift"),				kstPtO,	kexptPoint);
	PreDefineSymbol(GrcStructName("shift", "x"),		kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("shift", "y"),		kst,	kexptMeas);
	//	bogus attributes for error detection:
	PreDefineSymbol(GrcStructName("shift", "gpath"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("shift", "gpoint"),	kst, kexptNumber);
	PreDefineSymbol(GrcStructName("shift", "xoffset"),	kst, kexptMeas);
	PreDefineSymbol(GrcStructName("shift", "yoffset"),	kst, kexptMeas);

	PreDefineSymbol(GrcStructName("measure", "startofline"),kst, kexptMeas);
	PreDefineSymbol(GrcStructName("measure", "endofline"),	kst, kexptMeas);

	PreDefineSymbol(GrcStructName("justify", "stretch"),	kst, kexptMeas);
	PreDefineSymbol(GrcStructName("justify", "stretchHW"),	kst, kexptMeas);
	PreDefineSymbol(GrcStructName("justify", "shrink"),		kst, kexptMeas);
	PreDefineSymbol(GrcStructName("justify", "step"),		kst, kexptMeas);
	PreDefineSymbol(GrcStructName("justify", "weight"),		kst, kexptNumber);
	PreDefineSymbol(GrcStructName("justify", "width"),		kst, kexptMeas);

	for (int iLevel = 0; iLevel <= kMaxJustLevel; iLevel++)
	{
		auto staLevel = std::to_string(iLevel);
		PreDefineSymbol(GrcStructName("justify", staLevel, "stretch"),	kst, kexptMeas);
		PreDefineSymbol(GrcStructName("justify", staLevel,	"stretchHW"),kst,kexptMeas);
		PreDefineSymbol(GrcStructName("justify", staLevel, "shrink"),	kst, kexptMeas);
		PreDefineSymbol(GrcStructName("justify", staLevel, "step"),	kst, kexptMeas);
		PreDefineSymbol(GrcStructName("justify", staLevel, "weight"),	kst, kexptNumber);
		PreDefineSymbol(GrcStructName("justify", staLevel, "width"),	kst, kexptMeas);
	}

	PreDefineSymbol(GrcStructName("collision", "flags"),		kst,	kexptNumber);
	PreDefineSymbol(GrcStructName("collision", "status"),		kst,	kexptNumber); // alias for flags
	PreDefineSymbol(GrcStructName("collision", "min"),			kstPt,	kexptPoint);
	PreDefineSymbol(GrcStructName("collision", "min", "x"),		kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("collision", "min", "y"),		kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("collision", "max"),			kstPt,	kexptPoint);
	PreDefineSymbol(GrcStructName("collision", "max", "x"),		kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("collision", "max", "y"),		kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("collision", "margin"),		kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("collision", "marginweight"),	kst,	kexptNumber);
	PreDefineSymbol(GrcStructName("collision", "exclude", "glyph"),			kst,	kexptGlyphID);
	PreDefineSymbol(GrcStructName("collision", "exclude", "offset"),		kst,	kexptPoint);
	PreDefineSymbol(GrcStructName("collision", "exclude", "offset", "x"),	kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("collision", "exclude", "offset", "y"),	kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("collision", "fix", "x"),			kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("collision", "fix", "y"),			kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("sequence", "class"),				kst,	kexptNumber);
	PreDefineSymbol(GrcStructName("sequence", "proxClass"),			kst,	kexptNumber);
	PreDefineSymbol(GrcStructName("sequence", "order"),				kst,	kexptNumber);
	PreDefineSymbol(GrcStructName("sequence", "above", "xoffset"),	kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("sequence", "above", "weight"),	kst,	kexptNumber);
	PreDefineSymbol(GrcStructName("sequence", "below", "xlimit"),	kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("sequence", "below", "weight"),	kst,	kexptNumber);
	PreDefineSymbol(GrcStructName("sequence", "valign", "height"),	kst,	kexptMeas);
	PreDefineSymbol(GrcStructName("sequence", "valign", "weight"),	kst,	kexptNumber);

	PreDefineSymbol(GrcStructName("segsplit"),	kst, kexptNumber);

	//	A psuedo-attribute that indicates that a given slot serves as the "key" for the pass,
	//	for optimization purposes.
	PreDefineSymbol(GrcStructName("passKeySlot"), kst, kexptBoolean);

}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitGlyphAttrs()
{
	//	These were first recorded as slot attributes:

	Symbol psym;
	psym = AddType2(GrcStructName("component"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("directionality"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("breakweight"), ksymtGlyphAttr);
	psym->m_fGeneric = true;

	psym = AddType2(GrcStructName("justify", "stretch"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("justify", "shrink"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("justify", "step"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("justify", "weight"), ksymtGlyphAttr);
	psym->m_fGeneric = true;

	for (int iLevel = 0; iLevel <= kMaxJustLevel; iLevel++)
	{
		auto staLevel = std::to_string(iLevel);
		psym = AddType2(GrcStructName("justify", staLevel, "stretch"), ksymtGlyphAttr);
		psym->m_fGeneric = true;
		psym = AddType2(GrcStructName("justify", staLevel, "shrink"), ksymtGlyphAttr);
		psym->m_fGeneric = true;
		psym = AddType2(GrcStructName("justify", staLevel, "step"), ksymtGlyphAttr);
		psym->m_fGeneric = true;
		psym = AddType2(GrcStructName("justify", staLevel, "weight"), ksymtGlyphAttr);
		psym->m_fGeneric = true;
	}

	//	A fake glyph attribute that is used to store the actual glyph ID for pseudo-glyphs.
	psym = PreDefineSymbol(GrcStructName("*actualForPseudo*"), ksymtGlyphAttr, kexptNumber);
	psym->m_fGeneric = true;

	//	A built-in glyph attribute that is hold the pass optimization flags (bitmap indicating
	//	the passes for which a glyph is not a key glyph).
	psym = PreDefineSymbol(GrcStructName("*skipPasses*"), ksymtGlyphAttr, kexptNumber);
	psym->m_fGeneric = true;

	// These are just glyph attributes. These two must be in this order and contiguous,
	// so that the internal IDs are assigned correctly.

	psym = PreDefineSymbol(GrcStructName("mirror", "glyph"), ksymtGlyphAttr, kexptGlyphID);
	psym->m_fGeneric = true;
	psym = PreDefineSymbol(GrcStructName("mirror", "isEncoded"), ksymtGlyphAttr, kexptBoolean);
	psym->m_fGeneric = true;

	// Built-in glyph attributes that define how automatic collision fixing should work.
	psym = AddType2(GrcStructName("collision", "flags"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("collision", "min", "x"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("collision", "max", "x"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("collision", "min", "y"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("collision", "max", "y"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("collision", "margin"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("collision", "marginweight"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	// Not defined as glyph attributes:
	//psym = AddType2(GrcStructName("collision", "exclude", "glyph"), ksymtGlyphAttr);
	//psym->m_fGeneric = true;
	//psym = AddType2(GrcStructName("collision", "exclude", "offset", "x"), ksymtGlyphAttr);
	//psym->m_fGeneric = true;
	//psym = AddType2(GrcStructName("collision", "exclude", "offset", "y"), ksymtGlyphAttr);
	//psym->m_fGeneric = true;

	psym = AddType2(GrcStructName("sequence", "class"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("sequence", "proxClass"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("sequence", "order"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("sequence", "above", "xoffset"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("sequence", "above", "weight"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("sequence", "below", "xlimit"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("sequence", "below", "weight"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("sequence", "valign", "height"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	psym = AddType2(GrcStructName("sequence", "valign", "weight"), ksymtGlyphAttr);
	psym->m_fGeneric = true;
	
	// This one is used by the compiler, but not stored in the font:
	psym = PreDefineSymbol(GrcStructName("collision", "complexFit"), ksymtGlyphAttr, kexptNumber);
	psym->m_fGeneric = true;
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitSpecial()
{
	SymbolType kst = ksymtSpecial;

	PreDefineSymbol(GrcStructName("@"),	ksymtSpecialAt);
	PreDefineSymbol(GrcStructName("^"),	ksymtSpecialCaret);
	PreDefineSymbol(GrcStructName("#"),	ksymtSpecialLb);
	PreDefineSymbol(GrcStructName("_"), ksymtSpecialUnderscore);

	AddType2(GrcStructName("?"), kst);
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitTableTypes()
{
	SymbolType kst = ksymtTableRule;

	PreDefineSymbol(GrcStructName("linebreak"),			kst);
	PreDefineSymbol(GrcStructName("substitution"),		kst);
	PreDefineSymbol(GrcStructName("justification"),		kst);
	PreDefineSymbol(GrcStructName("positioning"),		kst);

	kst = ksymtTable;

	PreDefineSymbol(GrcStructName("feature"),			kst);
	PreDefineSymbol(GrcStructName("glyph"),				kst);
	PreDefineSymbol(GrcStructName("name"),				kst);
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitUnits()
{
	PreDefineSymbol(GrcStructName("m"),	ksymtUnit);
}

/*--------------------------------------------------------------------------------------------*/
void GrcSymbolTable::InitProcStates()
{
	SymbolType kst = ksymtProcState;

	PreDefineSymbol(GrcStructName("JustifyMode"),		kst, kexptNumber);
	//PreDefineSymbol(GrcStructName("JustifyLevel"),	kst, kexptNumber);

	PreDefineSymbol(GrcStructName("RunDirection"),		kst, kexptNumber);
}
