public class Code

//----- shortcuts to the relevant IL instruction codes of System.Reflection.Emit.OpCodes
public static readonly OpCode
// loading method arguments
LDARG0 = OpCodes.Ldarg_0,
LDARG1 = OpCodes.Ldarg_1,
LDARG2 = OpCodes.Ldarg_2,
LDARG3 = OpCodes.Ldarg_3,
LDARG = OpCodes.Ldarg_S,
// storing in method argument slots
STARG = OpCodes.Starg_S,
// loading local variables
LDLOC0 = OpCodes.Ldloc_0,
LDLOC1 = OpCodes.Ldloc_1,
LDLOC2 = OpCodes.Ldloc_2,
LDLOC3 = OpCodes.Ldloc_3,
LDLOC = OpCodes.Ldloc_S,
// storing local variables
STLOC0 = OpCodes.Stloc_0,
STLOC1 = OpCodes.Stloc_1,
STLOC2 = OpCodes.Stloc_2,
STLOC3 = OpCodes.Stloc_3,
STLOC = OpCodes.Stloc_S,
// loading constant values
LDNULL = OpCodes.Ldnull,
LDCM1 = OpCodes.Ldc_I4_M1,
LDC0 = OpCodes.Ldc_I4_0,
LDC1 = OpCodes.Ldc_I4_1,
LDC2 = OpCodes.Ldc_I4_2,
LDC3 = OpCodes.Ldc_I4_3,
LDC4 = OpCodes.Ldc_I4_4,
LDC5 = OpCodes.Ldc_I4_5,
LDC6 = OpCodes.Ldc_I4_6,
LDC7 = OpCodes.Ldc_I4_7,
LDC8 = OpCodes.Ldc_I4_8,
LDC = OpCodes.Ldc_I4,
// stack manipulation
DUP = OpCodes.Dup,
POP = OpCodes.Pop,
// method invocation
CALL = OpCodes.Call,
RET = OpCodes.Ret,
// branching
BR = OpCodes.Br,
BEQ = OpCodes.Beq,
BGE = OpCodes.Bge,
BGT = OpCodes.Bgt,
BLE = OpCodes.Ble,
BLT = OpCodes.Blt,
BNE = OpCodes.Bne_Un,
// arithmetics
ADD = OpCodes.Add,
SUB = OpCodes.Sub,
MUL = OpCodes.Mul,
DIV = OpCodes.Div,
REM = OpCodes.Rem,
NEG = OpCodes.Neg,
// field access
LDFLD = OpCodes.Ldfld,
STFLD = OpCodes.Stfld,
LDSFLD = OpCodes.Ldsfld,
STSFLD = OpCodes.Stsfld,
// object creation
NEWOBJ = OpCodes.Newobj,
NEWARR = OpCodes.Newarr,
// array handling 
LDLEN = OpCodes.Ldlen,
LDELEMCHR = OpCodes.Ldelem_U2,
LDELEMINT = OpCodes.Ldelem_I4,
LDELEMREF = OpCodes.Ldelem_Ref,
STELEMCHR = OpCodes.Stelem_I2,
STELEMINT = OpCodes.Stelem_I4,
STELEMREF = OpCodes.Stelem_Ref,

// exception handling
THROW = OpCodes.Throw;
const FieldAttributes GLOBALATTR = FieldAttributes.Assembly | FieldAttributes.Static;
const FieldAttributes FIELDATTR = FieldAttributes.Assembly;
const MethodAttributes METHATTR = MethodAttributes.Assembly | MethodAttributes.Static;
const TypeAttributes INNERATTR = TypeAttributes.Class | TypeAttributes.NotPublic;
const typeattributes progattr = typeattributes.class | typeattributes.public;
/* quick access to conditional branch instructions */
static readonly OpCode[] brtrue = { BEQ, BGE, BGT, BLE, BLT, BNE };
static readonly OpCode[] brfalse = { BNE, BLT, BLE, BGT, BGE, BEQ };

/* No-arg contructor of class System.Object. */ 
static readonly ConstructorInfo objCtor = typeof(object).GetConstructor(new Type[0]);

/* no-arg constructor of class system.executionengineexception, for functions without return */
internal static readonly ConstructorInfo eeexCtor = 
typeof(system.executionengineexception).getconstructor(new type[0]);
//----- System.Reflection.Emit objects for metadata management 

static AssemblyBuilder assembly; // metadata builder for the program assembly
static ModuleBuilder module; // metadata builder for the program module
static TypeBuilder program; // metadata builder for the main class
static TypeBuilder inner; // metadata builder for the currently compiled inner class
internal static MethodBuilder // builders for the basic I/O operations provided by the Z# keywords read and write
readChar, readInt, writeChar, writeInt;
internal static ILGenerator il; // IL stream of currently compiled method
//----- metadata generation

/* Creates the required metadata builder objects for the given Symbol.
* Call this after you inserted you Symbol into the symbol table.     // TAB
*/
internal static void CreateMetadata (Symbol sym) {
switch (sym.kind) {
case Symbol.Kinds.Global:
if (sym.type != Tab.noType) 
sym.fld = program.DefineField(sym.name, sym.type.sysType, GLOBALATTR);
break;
case Symbol.Kinds.Field:
if (sym.type != Tab.noType)
sym.fld = inner.DefineField(sym.name, sym.type.sysType, FIELDATTR);
break;
case Symbol.Kinds.Local:
il.DeclareLocal(sym.type.sysType);
break;
case Symbol.Kinds.Type:
inner = module.DefineType(sym.name, INNERATTR);
sym.type.sysType = inner;

// define default contructor (calls base constructor)
sym.ctor = inner.defineconstructor(methodattributes.public,
CallingConventions.Standard, new Type[0]);
il = sym.ctor.GetILGenerator();
il.Emit(LDARG0);
il.Emit(CALL, objCtor);
il.Emit(RET);
break;
case Symbol.Kinds.Meth:
// build argument list
Type[] args = new Type[sym.nArgs];
Symbol arg = sym.locals;
while (arg != null && arg.kind == Symbol.Kinds.Arg) { 
args[arg.adr] = arg.type.sysType;
arg = arg.next;
}
sym.meth = program.DefineMethod(sym.name, METHATTR, sym.type.sysType, args);
il = sym.meth.GetILGenerator();
if (sym.name == "Main") assembly.SetEntryPoint(sym.meth);
break;
case Symbol.Kinds.Prog:
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = sym.name;

assembly = 
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.Save);
module = assembly.DefineDynamicModule(sym.name + "Module", sym.name + ".exe");
program = module.DefineType(sym.name, PROGATTR);

// initialize globals
inner = null;

// build methods for I/O keywords (read, write)
BuildReadChar();
BuildReadInt();
BuildWriteChar();
BuildWriteInt();
break;
}
}

/* Completes the system type of an inner class.
* Call this at end of class declaration. */
internal static void CompleteClass () { 
inner.CreateType();
inner = null;
}
// ---------- instruction generation

/* Load the operand x onto the expression stack. */
internal static void Load (Item x) {
/*---------------------------------*/
/*----- insert your code here -----*/
/*---------------------------------*/ 
}

/* Load an integer constant onto the expression stack. */
internal static void LoadConst (int n) {
/*---------------------------------*/
/*----- insert your code here -----*/
/*---------------------------------*/ 
}

/* Generate an assignment x = y. */
internal static void Assign (Item x, Item y) {
/*---------------------------------*/
/*----- insert your code here -----*/
/*---------------------------------*/ 
}

/* Generate an increment instruction that increments x by n. */
internal static void Inc (Item x, int n) {
/*---------------------------------*/
/*----- insert your code here -----*/
/*---------------------------------*/ 
}

/* Unconditional jump. */
internal static void Jump (Label lab) { 
/*---------------------------------*/
/*----- insert your code here -----*/
/*---------------------------------*/ 
}

/* True Jump. Generates conditional branch instruction. */
internal static void TJump (Item x) {
/*---------------------------------*/
/*----- insert your code here -----*/
/*---------------------------------*/ 
}

/* False Jump. Generates conditional branch instruction. */
internal static void FJump (Item x) {
/*---------------------------------*/
/*----- insert your code here -----*/
/*---------------------------------*/ 
}
/* Generate an executable .NET-PE-File. */
public static void WritePEFile () {
program.CreateType();
if (inner != null) inner.CreateType();
assembly.save(assembly.getname().name + ".exe");
}

static void BuildReadChar () {
// char read () {
readChar = program.DefineMethod("readc", MethodAttributes.Static, typeof(char), new Type[0]);
il = readChar.GetILGenerator();

// return (char) System.Console.Read();
il.EmitCall(CALL, typeof(Console).GetMethod("Read", new Type[0]), null);
il.Emit(OpCodes.Conv_U2);

// }
il.Emit(RET);
}

static void BuildReadInt () { 
// int readi ()
readInt = program.DefineMethod("readi", MethodAttributes.Static, typeof(int), new Type[0]);
il = readInt.GetILGenerator();

// bool neg = false;
LocalBuilder neg = il.DeclareLocal(typeof(bool));
il.Emit(LDC0);
il.Emit(STLOC0);

// int x = 0;
LocalBuilder x = il.DeclareLocal(typeof(int));
il.Emit(LDC0);
il.Emit(STLOC1);

// char ch = read();
LocalBuilder ch = il.DeclareLocal(typeof(char));
il.EmitCall(CALL, readChar, null);
il.Emit(STLOC2);

// if (c == '-') {
Label ifEnd = il.DefineLabel();
il.Emit(LDLOC2);
il.Emit(LDC, (int) '-');
il.Emit(BNE, ifEnd);

// neg = true;
il.Emit(LDC1);
il.Emit(STLOC0);

// c = ReadChar();
il.EmitCall(CALL, readChar, null);
il.Emit(STLOC2);

// }
il.MarkLabel(ifEnd);

// while ('0' <= c && c <= '9') {
Label whileStart = il.DefineLabel();
Label whileEnd = il.DefineLabel();
il.MarkLabel(whileStart);
il.Emit(LDC, (int) '0');
il.Emit(LDLOC2);
il.Emit(BGT, whileEnd);
il.Emit(LDLOC2);
il.Emit(LDC, (int) '9');
il.Emit(BGT, whileEnd);

// x = 10 * x + (int) (c-'0');
il.Emit(LDC, 10);
il.Emit(LDLOC1);
il.Emit(MUL);
il.Emit(LDLOC2);
il.Emit(LDC, (int) '0');
il.Emit(SUB);
il.Emit(ADD);
il.Emit(STLOC1);

// c = ReadChar();
il.EmitCall(CALL, readChar, null);
il.Emit(STLOC2);

// }
il.Emit(BR, whileStart);
il.MarkLabel(whileEnd);

// return neg ? -x : x;
ifEnd = il.DefineLabel();
Label elseBranch = il.DefineLabel();
il.Emit(LDLOC0);
il.Emit(OpCodes.Brfalse, elseBranch);
il.Emit(LDLOC1);
il.Emit(NEG);
il.Emit(BR, ifEnd);
il.MarkLabel(elseBranch);
il.Emit(LDLOC1);
il.MarkLabel(ifEnd);

// } 
il.Emit(RET);


static void BuildWriteChar () {
// void Write (char c, int width) 
writeChar = program.DefineMethod("write", MethodAttributes.Static,
typeof(void), 
new Type[] { typeof(char), typeof(int) });
il = writeChar.GetILGenerator();

// System.Console.Write(System.String.Format("{{0,{0}}}", width), c)
il.Emit(OpCodes.Ldstr, "{{0,{0}}}");
il.Emit(LDARG1);
il.Emit(OpCodes.Box, typeof(int));
il.EmitCall(CALL, typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(object) }), null);

il.Emit(LDARG0);
il.Emit(OpCodes.Box, typeof(char)); 
il.EmitCall(CALL, typeof(Console).GetMethod("Write", new Type[] { typeof(string), typeof(object) }), null);

// }
il.Emit(RET);
}

static void BuildWriteInt () {
// void write (int x, int width) 
writeInt = program.DefineMethod("write", MethodAttributes.Static,
typeof(void), 
new Type[] { typeof(int), typeof(int) });
il = writeInt.GetILGenerator();

// System.Console.Write(System.String.Format("{{0,{0}}}", width), x)
il.Emit(OpCodes.Ldstr, "{{0,{0}}}");
il.Emit(LDARG1);
il.Emit(OpCodes.Box, typeof(int));
il.EmitCall(CALL, typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(object) }), null);

il.Emit(LDARG0);
il.Emit(OpCodes.Box, typeof(int));
il.EmitCall(CALL, typeof(Console).GetMethod("Write", new Type[] { typeof(string), typeof(object) }), null);

// }
il.Emit(RET);
}

}
/* Z# Code Item.
* An item stores the attributes of an operand during code generation.
*/
class Item {
public enum Kinds { Const, Arg, Local, Static, Field, Stack, Elem, Meth, Cond }

public Kinds kind; // Const, Local, Static, Stack, Field, Elem, Method, Cond
public Struct type; // item type
public int val; // Const: value
public int adr; // Arg, Local: offset
public int relop; // Cond: token code of relational operator
public Symbol sym; // Field, Meth: node from symbol table
public Label tLabel, fLabel; // Cond: true jumps, false jumps

public Item (Symbol sym) {
type = sym.type; 
this.sym = sym;
switch (sym.kind) {
case Symbol.Kinds.Const: kind = Kinds.Const; val = sym.val; break;
case Symbol.Kinds.Arg: kind = Kinds.Arg; adr = sym.adr; break;
case Symbol.Kinds.Local: kind = Kinds.Local; adr = sym.adr; break;
case Symbol.Kinds.Global: kind = Kinds.Static; break; 
case Symbol.Kinds.Field: kind = Kinds.Field; break;
case Symbol.Kinds.Meth: kind = Kinds.Meth; break;
default: Parser.Errors.Error(ErrorStrings.CREATE_ITEM); break;
}
}

// special constructor for Const Items
public Item (int val) { kind = Kinds.Const; type = Tab.intType; this.val = val; }

// special constructor for Cond Items
public Item (int relop, Struct type) {
this.kind = Kinds.Cond; 
this.type = type;
this.relop = relop;
tLabel = Code.il.DefineLabel();
fLabel = Code.il.DefineLabel();
}

// special constructor for Stack Items
internal Item (Struct type) {
kind = Kinds.Stack;
this.type = type;
}
}