private void GenExpr(ParseTreeNode expr, System.Type expectedType, ILGenerator il, SymbolTable symbolTable) { Type deliveredType; if (expr.Term.Name == "stringLiteral") { deliveredType = typeof(string); il.Emit(OpCodes.Ldstr, expr.Token.ValueString); } else if (expr.Term.Name == "number") { if (expr.Token.Value is int) { deliveredType = typeof(int); il.Emit(OpCodes.Ldc_I4, (int)expr.Token.Value); } else { deliveredType = typeof(double); il.Emit(OpCodes.Ldc_R8, float.Parse(expr.Token.ValueString)); } } else if (expr.Term.Name == "binExpr") { Type innerExpectedType = TypeOfAny(symbolTable, expr.ChildNodes[0], expr.ChildNodes[2]); if (new string[] { "=", ">", "<", ">=", "<=", "!=", "~=", "not=", "and", "or", "xor" }.Contains(expr.ChildNodes[1].Term.Name)) { deliveredType = typeof(bool); } else { deliveredType = innerExpectedType; } GenExpr(expr.ChildNodes[0], innerExpectedType, il, symbolTable); GenExpr(expr.ChildNodes[2], innerExpectedType, il, symbolTable); if (deliveredType == typeof(bool)) { switch (expr.ChildNodes[1].Term.Name) { case "=": il.Emit(OpCodes.Ceq); break; case "<": il.Emit(OpCodes.Clt); break; case ">": il.Emit(OpCodes.Cgt); break; case "<=": il.Emit(OpCodes.Cgt); il.Emit(OpCodes.Not); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.And); break; case ">=": il.Emit(OpCodes.Clt); il.Emit(OpCodes.Not); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.And); break; default: throw new Exception("Unrecognized operator " + expr.ChildNodes[1].Term.Name); } } else if (deliveredType == typeof(string)) { switch (expr.ChildNodes[1].Term.Name) { case "+": il.Emit(OpCodes.Call, typeof(System.String).GetMethod("Concat", new System.Type[] { typeof(string), typeof(string) })); break; default: throw new Exception("Unrecognized operator " + expr.ChildNodes[1].Term.Name); } } else { switch (expr.ChildNodes[1].Term.Name) { case "+": il.Emit(OpCodes.Add); break; case "*": il.Emit(OpCodes.Mul); break; case "-": il.Emit(OpCodes.Sub); break; case "/": il.Emit(OpCodes.Div); break; case "div": il.Emit(OpCodes.Div); expectedType = typeof(int); break; case "mod": il.Emit(OpCodes.Rem); break; default: throw new Exception("Unrecognized operator " + expr.ChildNodes[1].Term.Name); } } } else if (expr.Term.Name == "identifier") { string ident = expr.Token.ValueString; symbolTable.PushVar(ident, il); deliveredType = TypeOfExpr(expr, symbolTable); if (deliveredType == typeof(float)) { throw new NotImplementedException(); } } else if (expr.Term.Name == "functionCall" | expr.Term.Name == "memberCall") { deliveredType = TypeOfExpr(expr, symbolTable); if (deliveredType == typeof(float)) { throw new NotImplementedException(); } string funcName = GetIdentifier(expr); if (!symbolTable.functionTable.ContainsKey(funcName)) { if (symbolTable.HasVar(funcName) && symbolTable.TypeOfVar(funcName).IsArray) { //this is an array, return the appropriate value symbolTable.PushVar(funcName, il); if (expr.ChildNodes[1].ChildNodes.Count > 1) { throw new NotImplementedException("Multi-Dimensional arrays are not yet supported"); } this.GenExpr(expr.ChildNodes[1].ChildNodes[0], typeof(int), il, symbolTable); il.Emit(OpCodes.Stelem, symbolTable.TypeOfVar(funcName).GetElementType()); } else { throw new System.Exception("undeclared function or procedure '" + funcName + "'"); } } else { var parameters = symbolTable.functionTable[funcName].arguments; int currentArgument = 0; foreach (var arg in GetArgs(expr))//expr.ChildNodes[1].ChildNodes) { this.GenExpr(arg, parameters[currentArgument].argType, il, symbolTable); currentArgument++; } il.Emit(OpCodes.Call, symbolTable.functionTable[funcName].methodDefinition); } } else if (expr.Term.Name == "initExpr") { deliveredType = TypeOfAny(symbolTable, expr.ChildNodes[0].ChildNodes.ToArray()); int arraySize = expr.ChildNodes[0].ChildNodes.Count; //LocalBuilder paramValues = il.DeclareLocal(deliveredType.MakeArrayType()); //paramValues.SetLocalSymInfo("parameters"); il.Emit(OpCodes.Ldc_I4_S, arraySize); il.Emit(OpCodes.Newarr, deliveredType); //il.Emit(OpCodes.Stloc, paramValues); for (int i = 0; i < expr.ChildNodes[0].ChildNodes.Count; i++) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldc_I4, i); GenExpr(expr.ChildNodes[0].ChildNodes[i], deliveredType, il, symbolTable); il.Emit(OpCodes.Stelem, deliveredType); } deliveredType = deliveredType.MakeArrayType(); } else if (expr.Term.Name == "skip") { deliveredType = typeof(string); il.Emit(OpCodes.Ldstr, Environment.NewLine); } else { throw new System.Exception("don't know how to generate " + expr.Term.Name); } if (deliveredType != expectedType) { if (deliveredType == typeof(int) && expectedType == typeof(string)) { il.Emit(OpCodes.Box, typeof(int)); il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString")); } else if (deliveredType == typeof(double) && expectedType == typeof(string)) { il.Emit(OpCodes.Box, typeof(double)); il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString")); } else if (expectedType == null)//if the expected type is null then it doesn't matter what you give it { } else { throw new System.Exception("can't coerce a " + deliveredType.Name + " to a " + expectedType.Name); } } }
/*private void Store(string name, System.Type type, ref ILGenerator il, SymbolTable symbolTable) * { * if (symbolTable.locals.ContainsKey(name)) * { * LocalBuilder locb = symbolTable.locals[name]; * * if (locb.LocalType == type) * { * il.Emit(OpCodes.Stloc, symbolTable.locals[name]); * } * else * { * throw new System.Exception("'" + name + "' is of type " + locb.LocalType.Name + " but attempted to store value of type " + type.Name); * } * } * else * { * throw new System.Exception("undeclared variable '" + name + "'"); * } * }*/ private void GenExpr(ParseTreeNode expr, System.Type expectedType, ILGenerator il, SymbolTable symbolTable) { Type deliveredType; if (expr.Term.Name == "stringLiteral") { deliveredType = typeof(string); il.Emit(OpCodes.Ldstr, expr.Token.ValueString); } else if (expr.Term.Name == "number") { if (expr.Token.Value is int) { deliveredType = typeof(int); il.Emit(OpCodes.Ldc_I4, (int)expr.Token.Value); } else { deliveredType = typeof(float); il.Emit(OpCodes.Ldc_R4, float.Parse(expr.Token.ValueString)); } } else if (expr.Term.Name == "binExpr") { deliveredType = TypeOfExpr(expr.ChildNodes[0], symbolTable); GenExpr(expr.ChildNodes[0], deliveredType, il, symbolTable); GenExpr(expr.ChildNodes[2], deliveredType, il, symbolTable); switch (expr.ChildNodes[1].Term.Name) { case "+": il.Emit(OpCodes.Add); break; case "*": il.Emit(OpCodes.Mul); break; case "-": il.Emit(OpCodes.Sub); break; case "/": il.Emit(OpCodes.Div); break; case "mod": il.Emit(OpCodes.Rem); break; default: throw new Exception("Unrecognized operator " + expr.ChildNodes[1].Term.Name); } } else if (expr.Term.Name == "identifier") { string ident = expr.Token.ValueString; symbolTable.PushVar(ident, ref il); deliveredType = this.TypeOfExpr(expr, symbolTable); } else if (expr.Term.Name == "functionCall") { deliveredType = TypeOfExpr(expr, symbolTable); string funcName = expr.ChildNodes[0].Token.ValueString; if (!this.functionTable.ContainsKey(funcName)) { throw new System.Exception("undeclared function or procedure '" + funcName + "'"); } var parameters = this.functionTable[funcName].arguments; int currentArgument = 0; if (expr.ChildNodes[1].ChildNodes.Count > 0) {//push all the arguments onto the stack ParseTreeNode argItem = expr.ChildNodes[1]; while (true) { this.GenExpr(argItem.ChildNodes[0], parameters[currentArgument].argType, il, symbolTable); if (argItem.ChildNodes.Count == 1) { break; } argItem = argItem.ChildNodes[1]; currentArgument++; } } il.Emit(OpCodes.Call, this.functionTable[funcName].methodDefinition); } else { throw new System.Exception("don't know how to generate " + expr.GetType().Name); } if (deliveredType != expectedType) { if (deliveredType == typeof(int) && expectedType == typeof(string)) { il.Emit(OpCodes.Box, typeof(int)); il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString")); } else if (deliveredType == typeof(float) && expectedType == typeof(string)) { il.Emit(OpCodes.Box, typeof(float)); il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString")); } else { throw new System.Exception("can't coerce a " + deliveredType.Name + " to a " + expectedType.Name); } } }
private void GenStmt(ParseTreeNode stmt, ILGenerator il, SymbolTable symbolTable, Label?exitScope = null) { if (stmt.Term.Name == "program") { if (stmt.ChildNodes.Count > 0) { this.GenStmt(stmt.ChildNodes[0].ChildNodes[0], il, symbolTable); this.GenStmt(stmt.ChildNodes[1], il, symbolTable); } } else if (stmt.Term.Name == "variableDeclaration") { Type localType; // declare a local if (stmt.ChildNodes[2].Term.Name == "typeSpecifier") { localType = this.TypeOfTypeDeclaration(stmt.ChildNodes[2].ChildNodes[0]); } else { localType = TypeOfExpr(stmt.ChildNodes[2].ChildNodes[1], symbolTable); } Action <string> generateAssign = null; ParseTreeNode assign = stmt.ChildNodes.Where(x => x.Term.Name == "setEqual").SingleOrDefault(); // set the initial value if (assign != null) { generateAssign = new Action <string>(name => { this.GenExpr(assign.ChildNodes[1], symbolTable.locals[name].LocalType, il, symbolTable); symbolTable.Store(name, TypeOfExpr(assign.ChildNodes[1], symbolTable), il); }); } var variableIden = stmt.ChildNodes[1]; while (true) { string name = variableIden.ChildNodes[0].Token.ValueString; symbolTable.AddLocal(name, il.DeclareLocal(localType)); if (generateAssign != null) { generateAssign(name); } if (variableIden.ChildNodes.Count < 2) { break; } variableIden = variableIden.ChildNodes[1]; } } else if (stmt.Term.Name == "io") { if (stmt.ChildNodes[0].Token.ValueString == "put") { //the first argument is always there, until we can build a proper AST this'll have to do ParseTreeNode argItem = stmt.ChildNodes[1]; this.GenExpr(argItem.ChildNodes[0], typeof(string), il, symbolTable); il.Emit(OpCodes.Call, typeof(System.Console).GetMethod("Write", new System.Type[] { typeof(string) })); argItem = stmt.ChildNodes[2]; while (true) { if (argItem.ChildNodes.Count == 0) { break; } this.GenExpr(argItem.ChildNodes[0].ChildNodes[0], typeof(string), il, symbolTable); il.Emit(OpCodes.Call, typeof(System.Console).GetMethod("Write", new System.Type[] { typeof(string) })); argItem = argItem.ChildNodes[1]; } if (stmt.ChildNodes[3].ChildNodes.Count == 0)//put a newline character if there is no ... { il.Emit(OpCodes.Call, typeof(System.Console).GetMethod("WriteLine", new System.Type[] { })); } } else if (stmt.ChildNodes[0].Token.ValueString == "get") { foreach (var argument in stmt.ChildNodes[1].ChildNodes) { //switch(symbolTable.TypeOfVar( il.Emit(OpCodes.Call, typeof(System.Console).GetMethod("ReadLine", new System.Type[] { })); symbolTable.Store(argument.Token.ValueString, typeof(string), il); } } } else if (stmt.Term.Name == "assignment") { if (stmt.ChildNodes[0].Term.Name == "functionCall")//if we see this as a function call, we know that's not true, and it's actually an array (which is kinda the same thing in turing) { string arrayName = stmt.ChildNodes[0].ChildNodes[0].Token.ValueString; if (symbolTable.TypeOfVar(arrayName).IsArray) { symbolTable.PushVar(arrayName, il); if (stmt.ChildNodes[0].ChildNodes[1].ChildNodes.Count > 1) { throw new NotImplementedException("Multi-Dimensional arrays are not yet supported"); } this.GenExpr(stmt.ChildNodes[0].ChildNodes[1].ChildNodes[0], typeof(int), il, symbolTable); this.GenExpr(stmt.ChildNodes[1].ChildNodes[1], TypeOfExpr(stmt.ChildNodes[1].ChildNodes[1], symbolTable), il, symbolTable); il.Emit(OpCodes.Stelem, symbolTable.TypeOfVar(arrayName).GetElementType()); } else { throw new NotSupportedException(String.Format("Non-array identifier used like an array: {0}", arrayName)); } } else { this.GenExpr(stmt.ChildNodes[1].ChildNodes[1], TypeOfExpr(stmt.ChildNodes[1].ChildNodes[1], symbolTable), il, symbolTable); string ident = stmt.ChildNodes[0].Token.ValueString; symbolTable.Store(ident, TypeOfExpr(stmt.ChildNodes[1].ChildNodes[1], symbolTable), il); } } else if (stmt.Term.Name == "functionDefinition") { string functionName = stmt.ChildNodes[0].ChildNodes[1].Token.ValueString; SymbolTable localSymbols = new SymbolTable(symbolTable); foreach (var parameter in symbolTable.functionTable[functionName].arguments) { localSymbols.AddParameter(parameter.argName, parameter.argType); } var ilMeth = symbolTable.functionTable[functionName].GetILGenerator(); GenStmt(stmt.ChildNodes[1], ilMeth, localSymbols); ilMeth.Emit(OpCodes.Ret); } else if (stmt.Term.Name == "result") { GenExpr(stmt.ChildNodes[1], TypeOfExpr(stmt.ChildNodes[1], symbolTable), il, symbolTable); var result = il.DeclareLocal(TypeOfExpr(stmt.ChildNodes[1], symbolTable)); il.Emit(OpCodes.Stloc, result); il.Emit(OpCodes.Ldloc, result); il.Emit(OpCodes.Ret, result); } else if (stmt.Term.Name == "functionCall" | stmt.Term.Name == "memberCall") { GenExpr(stmt, null, il, symbolTable); } else if (stmt.Term.Name == "ifBlock") { Label ifTrue = il.DefineLabel(); Label ifFalse = il.DefineLabel(); Label endLabel = il.DefineLabel(); GenExpr(stmt.ChildNodes[0], typeof(bool), il, symbolTable); //expression to check if true il.Emit(OpCodes.Brtrue, ifTrue); //if true then jump to true block il.Emit(OpCodes.Br, ifFalse); //otherwise jump to false block il.MarkLabel(ifTrue); //true block GenStmt(stmt.ChildNodes[1], il, symbolTable); il.Emit(OpCodes.Br, endLabel); //jump to after false block il.MarkLabel(ifFalse); //false block if (stmt.ChildNodes[2].ChildNodes.Count > 0) //then there's an else-if, this takes place in the else section { ParseTreeNode elseBlockStmt = stmt.ChildNodes[2]; //Turn the elsif to an inner if statement elseBlockStmt.ChildNodes.Add(stmt.ChildNodes[3]); //Move the optional else statement to the inner if statement elseBlockStmt.Term.Name = "ifBlock"; GenStmt(elseBlockStmt, il, symbolTable); } else if (stmt.ChildNodes[3].ChildNodes.Count > 0) { GenStmt(stmt.ChildNodes[3].ChildNodes[0], il, symbolTable); //generate expresson for false section, otherwise the label will be at the same spot as the end } il.MarkLabel(endLabel); //the end of the if statement } else if (stmt.Term.Name == "loop") { Label beginLoop = il.DefineLabel(); Label endLoop = il.DefineLabel(); il.MarkLabel(beginLoop); GenStmt(stmt.ChildNodes[0], il, symbolTable, endLoop); il.Emit(OpCodes.Br, beginLoop); il.MarkLabel(endLoop); } else if (stmt.Term.Name == "forLoop") { il.BeginScope(); Label beginLoop = il.DefineLabel(); Label endLoop = il.DefineLabel(); LocalBuilder i = il.DeclareLocal(typeof(int)); string identName = stmt.ChildNodes[1].Token.ValueString; symbolTable.AddLocal(identName, i); symbolTable.AddLocal("___endLoop", il.DeclareLocal(typeof(int))); if (stmt.ChildNodes[2].ChildNodes.Count == 1)//then an identifier is used as a range, or char. We just fail for now { throw new NotImplementedException(); } else { GenExpr(stmt.ChildNodes[2].ChildNodes[0], typeof(int), il, symbolTable); symbolTable.Store(identName, typeof(int), il); GenExpr(stmt.ChildNodes[2].ChildNodes[1], typeof(int), il, symbolTable); symbolTable.Store("___endLoop", typeof(int), il); } il.MarkLabel(beginLoop); GenStmt(stmt.ChildNodes[4], il, symbolTable, endLoop); symbolTable.PushVar(identName, il); il.Emit(OpCodes.Ldc_I4_1); if (stmt.ChildNodes[3].ChildNodes.Count > 0)//then there is a decreasing statement, so do decreasing { il.Emit(OpCodes.Sub); } else { il.Emit(OpCodes.Add); } il.Emit(OpCodes.Dup); symbolTable.Store(identName, typeof(int), il); symbolTable.PushVar("___endLoop", il); if (stmt.ChildNodes[3].ChildNodes.Count > 0)//then there is a decreasing statement, so do decreasing { il.Emit(OpCodes.Bge, beginLoop); } else { il.Emit(OpCodes.Ble, beginLoop); } il.MarkLabel(endLoop); symbolTable.RemoveLocal(identName); symbolTable.RemoveLocal("___endLoop"); il.EndScope(); } else { throw new System.Exception("don't know how to gen a " + stmt.Term.Name); } }