using System;
using System.Reflection;
using System.Reflection.Emit;
namespace MicroCompiler
{
public class CodeGen
{
///
/// metadata builder for the program assembly
///
private readonly AssemblyBuilder fAssembly;
///
/// The parser is only used to print errors.
///
private readonly Parser fError;
///
/// metadata builder for the program module
///
private readonly ModuleBuilder fModule;
///
/// metadata builder for the main class
///
private readonly TypeBuilder fProgram;
///
/// IL stream of currently compiled method
///
private ILGenerator fIl;
///
/// Creates a code generator.
///
/// name for the assembly
/// only used for error reporting
public CodeGen(string target, Parser err)
{
fError = err;
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = target;
AppDomain cd = AppDomain.CurrentDomain;
fAssembly = cd.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.Save);
fModule = fAssembly.DefineDynamicModule(target + "Module",
target + ".exe", true);
fProgram = fModule.DefineType(target + "$",
TypeAttributes.Class | TypeAttributes.Public);
}
///
/// Creates the required metadata builder objects for the given Symbol.
/// Call this after you inserted your Symbol into the symbol table.
///
/// the symbol to create metadata for
public void CreateMetadata(Symbol sym)
{
switch (sym.Kind)
{
case SymbolKind.Local:
fIl.DeclareLocal(sym.Type);
break;
case SymbolKind.Func:
// build argument list
Type[] args = new Type[sym.NArgs];
for (int i = 0; i < sym.NArgs; ++i)
{
Symbol arg = sym.Locals[i];
args[arg.Adr] = arg.Type;
}
sym.Meth = fProgram.DefineMethod(sym.Name,
MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard, sym.Type, args);
fIl = sym.Meth.GetILGenerator();
if ("main".Equals(sym.Name))
{
fAssembly.SetEntryPoint(sym.Meth,
PEFileKinds.ConsoleApplication);
}
break;
default:
fError.SemErr("Unexpected symbol kind: " + sym.Kind);
break;
}
}
// ---------- instruction generation
///
/// Load the operand x onto the expression stack.
///
/// item to load
public void Load(Item x)
{
switch (x.Kind)
{
case ItemKind.Const:
LoadConst(x.Val);
break;
case ItemKind.Arg:
LoadArg(x.Adr);
break;
case ItemKind.Local:
LoadLocal(x.Adr);
break;
case ItemKind.Stack:
// nothing to do (already loaded)
break;
default:
fError.SemErr("Compiler error in Code.load, unexpected item kind");
break;
}
x.Kind = ItemKind.Stack;
}
///
/// Load the local variable on the given address
///
/// address of the argument to load
private void LoadLocal(int adr)
{
switch (adr)
{
case 0:
fIl.Emit(OpCodes.Ldloc_0);
break;
case 1:
fIl.Emit(OpCodes.Ldloc_1);
break;
case 2:
fIl.Emit(OpCodes.Ldloc_2);
break;
case 3:
fIl.Emit(OpCodes.Ldloc_3);
break;
default:
fIl.Emit(OpCodes.Ldloc, adr);
break;
}
}
///
/// Load the argument on the given address
///
/// address of the argument to load
private void LoadArg(int adr)
{
switch (adr)
{
case 0:
fIl.Emit(OpCodes.Ldarg_0);
break;
case 1:
fIl.Emit(OpCodes.Ldarg_1);
break;
case 2:
fIl.Emit(OpCodes.Ldarg_2);
break;
case 3:
fIl.Emit(OpCodes.Ldarg_3);
break;
default:
fIl.Emit(OpCodes.Ldarg, adr);
break;
}
}
///
/// Load an integer constant onto the expression stack.
///
/// integer constant to load
private void LoadConst(int n)
{
switch (n)
{
case -1:
fIl.Emit(OpCodes.Ldc_I4_M1);
break;
case 0:
fIl.Emit(OpCodes.Ldc_I4_0);
break;
case 1:
fIl.Emit(OpCodes.Ldc_I4_1);
break;
case 2:
fIl.Emit(OpCodes.Ldc_I4_2);
break;
case 3:
fIl.Emit(OpCodes.Ldc_I4_3);
break;
case 4:
fIl.Emit(OpCodes.Ldc_I4_4);
break;
case 5:
fIl.Emit(OpCodes.Ldc_I4_5);
break;
case 6:
fIl.Emit(OpCodes.Ldc_I4_6);
break;
case 7:
fIl.Emit(OpCodes.Ldc_I4_7);
break;
case 8:
fIl.Emit(OpCodes.Ldc_I4_8);
break;
default:
fIl.Emit(OpCodes.Ldc_I4, n);
break;
}
}
///
/// Generate an assignment x = y.
///
/// target of the assignment
/// value to assign
public void Assign(Item x, Item y)
{
// make sure y is loaded (if already on stack, nothing will happen)
Load(y);
switch (x.Kind)
{
case ItemKind.Arg:
fIl.Emit(OpCodes.Starg, x.Adr);
break;
case ItemKind.Local:
StoreLocal(x.Adr);
break;
default:
fError.SemErr(
"Left-hand side of an assignment must be a variable");
break;
}
}
///
/// Store the top stack element into a local variable.
///
/// the address of the target local variable
private void StoreLocal(int adr)
{
switch (adr)
{
case 0:
fIl.Emit(OpCodes.Stloc_0);
break;
case 1:
fIl.Emit(OpCodes.Stloc_1);
break;
case 2:
fIl.Emit(OpCodes.Stloc_2);
break;
case 3:
fIl.Emit(OpCodes.Stloc_3);
break;
default:
fIl.Emit(OpCodes.Stloc, adr);
break;
}
}
///
/// Generate an unconditional jump to the given label
///
/// target label
public void Jump(Label label)
{
fIl.Emit(OpCodes.Br, label);
}
///
/// True Jump. Generates conditional branch instruction.
///
/// condition item
public void TJump(Item x)
{
if (x.Kind != ItemKind.Cond)
{
throw new ArgumentException("item must be of kind condition");
}
fIl.Emit(x.Relop, x.Label);
}
///
/// Emits the given OpCode to the currently open method.
///
/// OpCode to emit
public void Emit(OpCode opcode)
{
fIl.Emit(opcode);
}
///
/// Emits a call to the given method.
///
/// Method to call
public void EmitCall(MethodInfo meth)
{
fIl.EmitCall(OpCodes.Call, meth, null);
}
///
/// Emits a call to the given symbol.
///
///
/// The function to call, the symbol must be
/// of kind .
///
public void EmitCall(Symbol function)
{
if (function.Kind != SymbolKind.Func)
{
fError.SemErr("Illegal function call to: " + function.Name
+ "(no function)");
return;
}
EmitCall(function.Meth);
}
///
/// Create a stack item with the given type
///
/// type of the item to create
/// the newly created item
public Item CreateStackItem(Type type)
{
Item item = new Item();
item.Kind = ItemKind.Stack;
item.Type = type;
return item;
}
///
/// Creates a constant item.
///
/// value of the item
/// integer type (int or char)
/// the newly created item
public Item CreateConstItem(int val, Type type)
{
Item item = new Item();
item.Kind = ItemKind.Const;
item.Val = val;
item.Type = type;
return item;
}
///
/// Creates an item for the given symbol
///
/// the symbol describing to item to create
/// the newly created item
public Item CreateItem(Symbol sym)
{
Item item = new Item();
item.Type = sym.Type;
item.Sym = sym;
switch (sym.Kind)
{
case SymbolKind.Arg:
item.Kind = ItemKind.Arg;
item.Adr = sym.Adr;
break;
case SymbolKind.Local:
item.Kind = ItemKind.Local;
item.Adr = sym.Adr;
break;
case SymbolKind.Func:
item.Kind = ItemKind.Func;
break;
default:
fError.SemErr(
"Cannot create code item for this kind of symbol table object");
break;
}
return item;
}
///
/// Creates an relational item.
///
/// relational operator to use
/// the newly created item
public Item CreateRelItem(OpCode relop)
{
Item item = new Item();
item.Kind = ItemKind.Cond;
item.Relop = relop;
item.Label = fIl.DefineLabel();
return item;
}
///
/// Create a new undefined label.
///
///
/// the newly created
public Label CreateLabel()
{
return fIl.DefineLabel();
}
///
/// Defines the given
///
/// the label to define
public void MarkLabel(Label label)
{
fIl.MarkLabel(label);
}
///
/// Generate an executable .NET-PE-File.
///
public void WritePEFile()
{
fProgram.CreateType();
if (fAssembly.EntryPoint == null)
{
fError.SemErr("Missing 'main' function");
}
fAssembly.Save(fAssembly.GetName().Name + ".exe");
}
}
///
/// Different item kinds.
///
public enum ItemKind
{
///
/// Constant
///
Const,
///
/// Function argument
///
Arg,
///
/// Local variable
///
Local,
///
/// Stack item
///
Stack,
///
/// Function
///
Func,
///
/// Conditional item
///
Cond
}
///
/// Simple data class.
/// An item stores the attributes of an operand
/// during code generation.
///
public class Item
{
private ItemKind kind;
private Type type;
private int val;
private int adr;
private OpCode relop;
private Symbol sym;
private Label label;
///
/// Any item: kind of the item.
///
public ItemKind Kind
{
get { return kind; }
set { kind = value; }
}
///
/// Any item: the system type of the item.
///
public Type Type
{
get { return type; }
set { type = value; }
}
///
/// Const: value
///
public int Val
{
get { return val; }
set { val = value; }
}
///
/// Arg, Local: offset
///
public int Adr
{
get { return adr; }
set { adr = value; }
}
///
/// Cond: OpCode of relational operator
///
public OpCode Relop
{
get { return relop; }
set { relop = value; }
}
///
/// Field, Func: node from symbol table
///
public Symbol Sym
{
get { return sym; }
set { sym = value; }
}
///
/// Cond: target
///
public Label Label
{
get { return label; }
set { label = value; }
}
}
}