using System; using System.Collections.Generic; using System.Reflection.Emit; namespace MicroCompiler { /// /// Symbol table, stores the named symbols in the program, /// and manages scopes. /// public class SymTab { /// /// Very simple scope data class. A scope knows its parent /// scope, and its child symbols. /// private class Scope { /// /// Pointer to parent scope. /// public Scope Outer; /// /// Local declarations. /// public readonly List Locals = new List(); /// /// Number of function arguments in this scope. /// public int NArgs; /// /// Number of local variables in this scope. /// public int NLocs; } /// /// current scope /// private Scope topScope; /// /// The parser is only used to print errors via /// . /// private readonly Parser fError; /// /// pointer to the integer symbol /// private readonly Symbol intSym; /// /// dummy variable, used when the searched variable can not be found /// private readonly Symbol dummyVar; /// /// Creates a symbol table, with a as error sink. /// /// the parser is only used as an error sink public SymTab(Parser err) { fError = err; // set up "universe" (= predefined names) topScope = new Scope(); intSym = Insert(SymbolKind.Type, "int", typeof (int)); Insert(SymbolKind.Type, "char", typeof (char)); dummyVar = new Symbol(SymbolKind.Local, "$dummy$", typeof (int)); } /// /// Opens a new scope. /// /// public void OpenScope() { Scope s = new Scope(); s.NArgs = 0; s.NLocs = 0; s.Outer = topScope; topScope = s; } /// /// Closes the current scope. /// /// public void CloseScope() { topScope = topScope.Outer; } /// /// Creates a new with the given properties. /// If problems emerge a error message will be printed and /// a dummy symbol will be created to avoid null pointers. /// /// /// kind of the symbol; like function, or local variable /// /// the name of the symbol /// type of the symbol; like int, or char /// the newly create symbol public Symbol Insert(SymbolKind kind, string name, Type type) { bool isDummy = false; if (string.IsNullOrEmpty(name)) { fError.SemErr("A name must not be null, or an empty string"); isDummy = true; name = "dummy"; } Symbol sym = new Symbol(kind, name, type); if (kind == SymbolKind.Arg) { if (topScope.NArgs >= 256) { fError.SemErr("Too many arguments"); isDummy = true; } else { sym.Adr = topScope.NArgs++; } } else if (kind == SymbolKind.Local) { if (topScope.NLocs >= 256) { fError.SemErr("Too many local variables"); isDummy = true; } else { sym.Adr = topScope.NLocs++; } } if (Find(name, kind) != null) { // only unique symbols are allowed fError.SemErr(name + " declared twice"); isDummy = true; } if (!isDummy) { // everything went fine, store the symbol topScope.Locals.Add(sym); } return sym; } /// /// Retrieves the type with the given name. /// /// name of the type to find /// /// the found type, or int if the /// searched type does not exist /// public Symbol FindType(string name) { Symbol sym = Find(name, SymbolKind.Type); if (sym == null) { fError.SemErr("Type not found: '" + name + "'"); sym = intSym; } return sym; } /// /// Retrieves the function with name from the innermost scope. /// /// name of the function to search /// /// the found function, or the function get_i if the searched /// function could not be found. /// public Symbol FindFunc(string name) { Symbol sym = Find(name, SymbolKind.Func); if (sym == null) { fError.SemErr("Function not found: '" + name + "'"); sym = FindFunc("get_i"); } return sym; } /// /// Retrieves the local variable, function parameter or constant /// with name from the innermost scope. /// /// the name of the searched variable /// /// the found variable, or a dummy int variable if the /// searched variable could not be found /// public Symbol FindVar(string name) { Symbol sym = Find(name, SymbolKind.Local | SymbolKind.Arg); if (sym == null) { fError.SemErr("Variable not found: '" + name + "'"); sym = dummyVar; } return sym; } /// /// Copy the locals of the current top scope to the given function. /// /// the target function public void SetLocals(Symbol function) { if (function.Kind != SymbolKind.Func) { throw new ArgumentException( "function must represent a function symbol"); } Symbol func = function; func.Locals = topScope.Locals; func.NArgs = topScope.NArgs; func.NLocs = topScope.NLocs; } #region private auxillary methods /// /// Search a symbol with the given name, in the given scope. /// /// name of the searched symbol /// scope to search in /// /// the found symbol, or null if the /// symbol is not in the given scope /// private static Symbol Find(string name, Scope scope) { foreach (Symbol sym in scope.Locals) { if (sym.Name == name) { return sym; } } return null; } /// /// Search a symbol with the given name, and symbol kind. /// /// name of the searched symbol /// kind if the searched symbol /// /// the found symbol, or null if the /// symbol could not be found /// private Symbol Find(string name, SymbolKind kind) { // look for name in current scope, the surrounding scope and so on. Scope scope = topScope; while (scope != null) { Symbol sym = Find(name, scope); if (sym != null && (sym.Kind & kind) == sym.Kind) { return sym; } scope = scope.Outer; } return null; } #endregion } /// /// Symbol kindes. /// [Flags] public enum SymbolKind { /// /// Function argument /// Arg = 1, /// /// Local variable /// Local = 2, /// /// Type, e.g. int, char /// Type = 4, /// /// Function /// Func = 8 } /// /// Symbol Table Nodes: /// Every named object in a program is stored in an Symbol node. /// Every scope has a list of Symbols declared within it. /// public class Symbol { private readonly SymbolKind kind; private readonly string name; private readonly Type type; private int adr; private int nArgs; private int nLocs; private MethodBuilder meth; private List locals; /// /// creates a symbol with the given attributes /// /// kind of the symbol /// name of the symbol /// system type of the symbol public Symbol(SymbolKind kind, string name, Type type) { this.kind = kind; this.name = name; this.type = type; } /// /// Any symbol: Kind of the symbol /// public SymbolKind Kind { get { return kind; } } /// /// Any symbol: Name of the symbol /// public string Name { get { return name; } } /// /// Any symbol: Type of the symbol /// public Type Type { get { return type; } } /// /// Arg, Local: order of declaration in scope /// public int Adr { get { return adr; } set { adr = value; } } /// /// Func: Number of arguments /// public int NArgs { get { return nArgs; } set { nArgs = value; } } /// /// Func: Number of local variables /// public int NLocs { get { return nLocs; } set { nLocs = value; } } /// /// Func: arguments, then local variables; /// public List Locals { get { return locals; } set { locals = value; } } /// /// Func: builder for metadata and CIL /// public MethodBuilder Meth { get { return meth; } set { meth = value; } } } }