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; }
}
}
}