📄 semanticchecker.cs
字号:
//-----------------------------------------------------------------------------
// File: SemanticChecker.cs
//
// Description:
// Provide a class to traverse the AST and populate the symbol table.
//
//-----------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Reflection;
using System.Collections;
using System.Xml;
using Blue.Public;
using Log = Blue.Log;
using ErrorLog = Blue.Utilities.ErrorLog;
namespace SymbolEngine
{
//-----------------------------------------------------------------------------
// Interface used by nodes during semantic resolution
//-----------------------------------------------------------------------------
/// <summary>
/// Interface used by all functions doing symbol resolution.
/// </summary>
/// <remarks>
/// This interface provides context for symbols, scope lookup functions,
/// type-utility functions, and safe exposure to an <see cref="ICLRtypeProvider"/>.
/// It is not exposed to the driver.
/// <para> The Driver starts symbol resolution via the <see cref="ISemanticChecker"/> interface.
/// Resolution is done entirely in:
/// <list type="number">
/// <item>ResolveXX() methods on the <see cref="AST.Node"/> class </item>
/// <item> Helper functions on the <see cref="SymEntry"/> classes. </item>
/// <item> The <see cref="SemanticChecker"/> class, which implements the ISemanticChecker. </item>
/// </list>
/// An ISemanticResolver interface exposes an API to allow these code sections
/// (and no other sections) to do symbol resolution.
/// </para>
/// </remarks>
public interface ISemanticResolver
{
//.....................................................................
// Context
//.....................................................................
// Sets the current context that we lookup symbols against.
// Returns the previous current context, which should be
// passed to RestoreContext()
Scope SetCurrentContext(Scope scopeNewContext);
Scope GetCurrentContext();
void RestoreContext(Scope scopePreviousContext);
// Lookup a symbol in the current context.
// The context includes the lexical scope stack, super scopes,
// and using directives
SymEntry LookupSymbolWithContext(Identifier strName, bool fMustExist);
// @todo - make this implicit in the Push & Pop scope
// Set the current class that we're processing
void SetCurrentClass(TypeEntry type);
TypeEntry GetCurrentClass();
void SetCurrentMethod(MethodExpEntry m);
MethodExpEntry GetCurrentMethod();
//.....................................................................
// Lookup.
//.....................................................................
// Lookup symbol in a particular scope (and its inherited scopes)
SymEntry LookupSymbol(Scope scope, Identifier id, bool fMustExist);
SymEntry LookupSymbol(Scope scope, string st, bool fMustExist); // deprecated
// Context-free
// Lookup a system type, always must exist (else we throw internal error).
TypeEntry LookupSystemType(string st);
// Get the CLR type for an array / ref type. This will go through IProvider
System.Type GetArrayType(ArrayTypeEntry sym);
System.Type GetRefToType(System.Type t); // get a reference type
// Lookup symbol in current scope stack
// For importing, need a mapping from CLR types to blue types
TypeEntry ResolveCLRTypeToBlueType(System.Type t);
void AddClrResolvedType(TypeEntry sym);
//.....................................................................
// Utility
//.....................................................................
// For type checking, make sure that tDerived is of type tBase.
// Throw an exception elsewise
void EnsureAssignable(AST.Exp expFrom, System.Type tTo);
void EnsureAssignable(System.Type tFrom, System.Type cTo, FileRange location);
// Ensure that a symbol is what we expect it to be (ie, we don't try to use a function name
// as a label);
// If match, returns the symbol passed in (for nesting purposes)
// Else throws an exception
SymEntry EnsureSymbolType(SymEntry sym, System.Type tExpected, FileRange location);
// Just checks, doesn't throw an exception either way
bool IsDerivedType(TypeEntry tBase, TypeEntry tDerived);
//.....................................................................
// Debugging
//.....................................................................
void Dump();
}
//-----------------------------------------------------------------------------
// Class to provide a context for semantic checking (symbol resolution)
//-----------------------------------------------------------------------------
/// <summary>
/// This class walks the AST to resolve all symbols.
/// </summary>
public class SemanticChecker : ISemanticChecker, ISemanticResolver
{
#region Checks
// Dump the entire symbol contents to an XML file
public void DumpSymbolTable(XmlWriter o)
{
o.WriteStartDocument();
o.WriteStartElement("SymbolTable");
if (m_scopeGlobal != null)
m_scopeGlobal.Dump(o, true);
o.WriteEndElement(); // SymbolTable
o.WriteEndDocument();
o.Close();
}
#endregion
#region Error Handling
//-----------------------------------------------------------------------------
// Error handling
//-----------------------------------------------------------------------------
// Shortcut helper functions.
public void PrintError(SymbolError.SymbolErrorException e)
{
Blue.Driver.StdErrorLog.PrintError(e);
}
public void ThrowError(SymbolError.SymbolErrorException e)
{
Blue.Driver.StdErrorLog.ThrowError(e);
}
public virtual void EnsureAssignable(
AST.Exp expFrom,
System.Type tTo
)
{
EnsureAssignable(expFrom.CLRType, tTo, expFrom.Location);
}
#endregion
#region Type Checking functions
public virtual void EnsureAssignable(
System.Type tFrom,
System.Type tTo,
FileRange location
)
{
bool fOk = TypeEntry.IsAssignable(tFrom, tTo);
if (!fOk)
{
ThrowError(SymbolError.TypeMismatch(tFrom, tTo, location));
}
}
// Just check. This is useful because sometimes we want to fail if something
// is a derived type (ex, CatchHandlers shouldn't be derived type of a previous handler)
public bool IsDerivedType(TypeEntry tBase, TypeEntry tDerived)
{
// Note that System.Array is a derived type for all arrays, so special case that
if (tBase.CLRType == typeof(System.Array))
{
if (tDerived.IsArray)
return true;
}
// @todo - For now, figure out how to safely convert System.Array to an array
if (tDerived.CLRType == typeof(System.Array))
{
if (tBase.CLRType == typeof(System.Array))
return true;
}
// Note that arrays may be different instances
// but non arrays have only one TypeEntry per type
// So peel off the arrays and get to the base type
while (tBase.IsArray)
{
if (!tDerived.IsArray)
return false;
tBase = tBase.AsArrayType.ElemType;
tDerived = tDerived.AsArrayType.ElemType;
}
// Compare innermost types
if (tDerived.IsArray)
{
return false;
}
// Now walk up tDerived's chain looking for tBase
TypeEntry t = tDerived;
do
{
if (tBase == t)
return true;
t = t.Super;
} while(t != null);
// Ok. So tBase is not a base class of tDerived. Check if tBase is an interface
// that tDerive implements
if (tBase.IsInterface)
{
if (IsBaseInterface(tBase, tDerived))
return true;
}
return false;
}
// Helper to check if tDerived implements the interface tBaseInterface.
protected bool IsBaseInterface(TypeEntry tBaseInterface, TypeEntry tDerived)
{
Debug.Assert(tBaseInterface.IsInterface);
if (tBaseInterface == tDerived)
return true;
// Check superclasses
if (tDerived.Super != null)
if (IsBaseInterface(tBaseInterface, tDerived.Super))
return true;
// Check base interfaces
foreach(TypeEntry ti in tDerived.BaseInterfaces)
{
if (IsBaseInterface(tBaseInterface, ti))
return true;
}
return false;
}
public SymEntry EnsureSymbolType(SymEntry sym, System.Type tExpected, FileRange location)
{
if (sym == null)
return null;
/*
Type tSym = sym.GetType();
if (tSym == tExpected)
return sym;
if (tSym.IsSubclassOf(tExpected))
return sym;
*/
bool fMatch = SymbolEngine.TypeEntry.IsAssignable(sym.GetType(), tExpected);
if (fMatch)
return sym;
ThrowError(SymbolError.BadSymbolType(sym, tExpected, location));
/*
this.ThrowError(Code.cBadSymbolType,
location,
"Symbol '" + sym.Name + "' must be of type '" + tExpected.ToString() + "', not '" +
sym.GetType().ToString() + "'");
*/
return sym;
}
#endregion
//-----------------------------------------------------------------------------
// Add aliases for the default types
// Must have loaded mscorlib.dll first
//-----------------------------------------------------------------------------
protected void AddDefaultTypes(Scope scopeGlobal)
{
// Alias
scopeGlobal.AddAliasSymbol("int", LookupSystemType("Int32"));
scopeGlobal.AddAliasSymbol("void", LookupSystemType("Void"));
scopeGlobal.AddAliasSymbol("char", LookupSystemType("Char"));
scopeGlobal.AddAliasSymbol("bool", LookupSystemType("Boolean"));
scopeGlobal.AddAliasSymbol("string", LookupSystemType("String"));
scopeGlobal.AddAliasSymbol("object", LookupSystemType("Object"));
// Ensure that compound types that are backed by a core clr type are resovled.
// (mainly Array, Enum, Delegate)
TypeEntry tArray = LookupSystemType("Array");
tArray.EnsureResolved(this);
TypeEntry tEnum = LookupSystemType("Enum");
tEnum.EnsureResolved(this);
TypeEntry tDelegate = LookupSystemType("MulticastDelegate");
tDelegate.EnsureResolved(this);
}
#region Importing Assemblies
//-----------------------------------------------------------------------------
// Create the scopes for an imported types
//-----------------------------------------------------------------------------
Scope CreateImportedContext(System.Type tImport)
{
// Traverse namespaces to find scope
Scope scope = m_scopeGlobal;
string s = tImport.ToString();
// In a type's string name, the '.' separates namespaces,
// the '+' separates for nested classes.
// Valid form:
// i.i.i+i+i+i
int iStart = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -