/// <summary> /// Creates a new FunctionDeclaration given a token and a parent scope /// </summary> /// <param name="t">Function declaration token</param> /// <param name="parentScope">Parent scope of this function</param> public FunctionDeclaration(Token t, Scope parentScope) : this(parentScope) { // Initialize private members this.t = t; this.name = t.val; }
/// <summary> /// Creates a new FunctionDeclaration given a parent scope /// </summary> /// <param name="parentScope">Parent scope of this function</param> private FunctionDeclaration(Scope parentScope) { // Initialize private members localScope = new LocalScope(parentScope); arguments = new List<BaseType>(); returnType = PrimitiveType.VOID; }
/// <summary> /// Evaluates this node and all of its children /// </summary> /// <param name="scope">The scope of this statement</param> /// <returns></returns> public override Statement Evaluate(Scope scope) { // Check if left-side expression is assignable if (!(leftSide is AssignableExpression)) { // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Left side cannot be assigned to"); return this; } // Evaluate both expressions leftSide = leftSide.Evaluate(scope); rightSide = rightSide.Evaluate(scope); return this; }
/// <summary> /// Evaluates this node and all children recursively /// </summary> /// <param name="scope">The scope of this statement</param> /// <returns>Statement object after evaluation</returns> public abstract Statement Evaluate(Scope scope);
/// <summary> /// Emits code for this expression /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> public override void EmitCode(ILGenerator ilGen, Scope scope) { // Code generation method is determined depending on type switch (((PrimitiveType)returnType).type) { // Emit code for boolean value case Primitive.Bool: if ((bool)value) { // If boolean is true, load 1 onto the stack (the CLR doesn't have boolean types) ilGen.Emit(OpCodes.Ldc_I4_1); } else { // If boolean is false, load 0 onto the stack ilGen.Emit(OpCodes.Ldc_I4_0); } break; // Emit code for string case Primitive.String: // Load string onto the stack ilGen.Emit(OpCodes.Ldstr, (string)value); break; // Emit code for integer value case Primitive.Int: // Cast to int int val = (int)value; // Perform CLR dependent optimiztion - use short form int constant loading switch (val) { case 0: ilGen.Emit(OpCodes.Ldc_I4_0); break; case 1: ilGen.Emit(OpCodes.Ldc_I4_1); break; case 2: ilGen.Emit(OpCodes.Ldc_I4_2); break; case 3: ilGen.Emit(OpCodes.Ldc_I4_3); break; case 4: ilGen.Emit(OpCodes.Ldc_I4_4); break; case 5: ilGen.Emit(OpCodes.Ldc_I4_5); break; case 6: ilGen.Emit(OpCodes.Ldc_I4_6); break; case 7: ilGen.Emit(OpCodes.Ldc_I4_7); break; case 8: ilGen.Emit(OpCodes.Ldc_I4_8); break; default: // If constant is greater than 8, use normal load instruction ilGen.Emit(OpCodes.Ldc_I4, (int)value); break; } break; // Emit code for double case Primitive.Double: // Load double value onto the stack ilGen.Emit(OpCodes.Ldc_R8, (double)value); break; default: break; } }
/// <summary> /// Emits code for this expression /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> public override void EmitCode(ILGenerator ilGen, Scope scope) { // Emit code for operand operand.EmitCode(ilGen, scope); // Only typecasts available are between primitive types if (returnType is PrimitiveType) { // Code is emitted depending on cast type switch (((PrimitiveType)returnType).type) { // Emit code for int cast case Primitive.Int: ilGen.Emit(OpCodes.Conv_I4); break; // Emit code for double cast case Primitive.Double: ilGen.Emit(OpCodes.Conv_R8); break; } } }
/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public override Expression Evaluate(Scope scope) { // Evaluate all arguments for (int i = 0; i < arguments.Count; i++) { arguments[i] = arguments[i].Evaluate(scope); } // Get signature from symbol table Signature sig = scope.GetFunction(this); // If no function is retrieved if (sig == null) { // Unknown function error Compiler.Compiler.errors.SemErr(t.line, t.col, "Unknown function or ambiguos call to " + methodName); } else { // Set return type this.returnType = sig.returnType; // Step through each argument for (int i = 0; i < sig.arguments.Count; i++) { // If argument expression type is different than expected argument // Arguments are guaranteed to be compatible since a matching signature was found in the symbol table if (!arguments[i].returnType.Equals(sig.arguments[i])) { // Create implicit type cast to expected argument Expression expr = arguments[i]; arguments[i] = new CastExpression(sig.arguments[i]); (arguments[i] as CastExpression).operand = expr; } } } return this; }
/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public override Expression Evaluate(Scope scope) { // Evaluate operands leftOperand = leftOperand.Evaluate(scope); rightOperand = rightOperand.Evaluate(scope); // Check if both operands are of the same type if (leftOperand.returnType.ToCLRType() != rightOperand.returnType.ToCLRType()) { // If not, check if left operand can be implicitely typecasted to right operand type if (leftOperand.returnType.IsCompatible(rightOperand.returnType)) { // Create implicit cast CastExpression implicitCast = new CastExpression(rightOperand.returnType); implicitCast.operand = leftOperand; // Evaluate typecast for folding this.leftOperand = implicitCast.Evaluate(scope); // Set return type this.returnType = rightOperand.returnType; } // If not, check if right operand can be implicitely typecasted to left operand type else if (rightOperand.returnType.IsCompatible(leftOperand.returnType)) { // Create implicit cast CastExpression implicitCast = new CastExpression(leftOperand.returnType); implicitCast.operand = rightOperand; // Evaluate for folding this.rightOperand = implicitCast.Evaluate(scope); // Set return type this.returnType = leftOperand.returnType; } else { // Types are incompatible - issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Incompatible types"); this.returnType = PrimitiveType.UNSUPPORTED; return this; } } // If operands are of the same type else { // Set return type this.returnType = leftOperand.returnType; } // If operand is not arithmetic, overwrite return type if ((int)op >= 6) { this.returnType = PrimitiveType.BOOL; } // Check if operator and operands are compatible switch (op) { // Addition is accepted for strings and numeric types case BinaryOperator.Add: // If type is not string, check if it is numerical if (!leftOperand.returnType.Equals(PrimitiveType.STRING)) { goto case BinaryOperator.Leq; } break; // Other arithmetic operators and comparisons (except equals and not equals) are accepted for numeric types case BinaryOperator.Sub: case BinaryOperator.Mul: case BinaryOperator.Div: case BinaryOperator.Gt: case BinaryOperator.Lt: case BinaryOperator.Geq: case BinaryOperator.Leq: // Check if type is not integer or double if (!leftOperand.returnType.Equals(PrimitiveType.INT) && !leftOperand.returnType.Equals(PrimitiveType.DOUBLE)) { // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Arithmetic operator can only be applied to numerical types"); } break; // Modulo operator is accepted only for integer type case BinaryOperator.Rem: // Check if type is not integer if (!leftOperand.returnType.Equals(PrimitiveType.INT)) { // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Reminder operator can only be applied to integer type"); } break; // Equality and non equality are accepted for all types case BinaryOperator.Eq: case BinaryOperator.Neq: break; // Default case stands for logical operators default: // Check if type is not boolean if (!leftOperand.returnType.Equals(PrimitiveType.BOOL)) { // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Logical operator can only be applied to boolean type"); } break; } // If both operands are constant expressions, perform constant folding if ((leftOperand is ConstantExpression) && (rightOperand is ConstantExpression)) { // Compile time evaluation of expressions is done based on type and operator // If type is boolean if (leftOperand.returnType.Equals(PrimitiveType.BOOL)) { switch (op) { // Compute equality case BinaryOperator.Eq: // Cast values to bool, compare and create new constant expression return new ConstantExpression(Primitive.Bool, (bool)(leftOperand as ConstantExpression).value == (bool)(rightOperand as ConstantExpression).value); // Compute non-equality case BinaryOperator.Neq: // Same as exclusive or for boolean values goto case BinaryOperator.Xor; // Compute logical and case BinaryOperator.And: // Cast values to bool, apply operation and create constant expression return new ConstantExpression(Primitive.Bool, (bool)(leftOperand as ConstantExpression).value && (bool)(rightOperand as ConstantExpression).value); // Compute logical or case BinaryOperator.Or: // Cast values to bool, apply operation and create constant expression return new ConstantExpression(Primitive.Bool, (bool)(leftOperand as ConstantExpression).value || (bool)(rightOperand as ConstantExpression).value); // Compute logical exclusive or case BinaryOperator.Xor: // Cast values to bool, apply operation and create constant expression (xor is equivalent to a // non-equality check) return new ConstantExpression(Primitive.Bool, (bool)(leftOperand as ConstantExpression).value != (bool)(rightOperand as ConstantExpression).value); } } // If type is double else if (leftOperand.returnType.Equals(PrimitiveType.DOUBLE)) { switch (op) { // Compute equality case BinaryOperator.Eq: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value == (double)(rightOperand as ConstantExpression).value); // Compute non-equality case BinaryOperator.Neq: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value != (double)(rightOperand as ConstantExpression).value); // Compute greater than case BinaryOperator.Gt: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value > (double)(rightOperand as ConstantExpression).value); // Compute greater than or equal to case BinaryOperator.Geq: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value >= (double)(rightOperand as ConstantExpression).value); // Compute less than case BinaryOperator.Lt: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value < (double)(rightOperand as ConstantExpression).value); // Compute less than or equal to case BinaryOperator.Leq: // Cast values to double, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (double)(leftOperand as ConstantExpression).value <= (double)(rightOperand as ConstantExpression).value); // Compute addition case BinaryOperator.Add: // Cast values to double, apply operation and create constant expression return new ConstantExpression(Primitive.Double, ((double)(leftOperand as ConstantExpression).value + (double)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute subtraction case BinaryOperator.Sub: // Cast values to double, apply operation and create constant expression return new ConstantExpression(Primitive.Double, ((double)(leftOperand as ConstantExpression).value - (double)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute multiplication case BinaryOperator.Mul: // Cast values to double, apply operation and create constant expression return new ConstantExpression(Primitive.Double, ((double)(leftOperand as ConstantExpression).value * (double)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute division case BinaryOperator.Div: // Cast values to double, apply operation and create constant expression return new ConstantExpression(Primitive.Double, ((double)(leftOperand as ConstantExpression).value / (double)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); } } // If type is integer else if (leftOperand.returnType.Equals(PrimitiveType.INT)) { switch (op) { // Compute equality case BinaryOperator.Eq: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value == (int)(rightOperand as ConstantExpression).value); // Compute non-equality case BinaryOperator.Neq: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value != (int)(rightOperand as ConstantExpression).value); // Compute greater than case BinaryOperator.Gt: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value > (int)(rightOperand as ConstantExpression).value); // Compute greater than or equal to case BinaryOperator.Geq: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value >= (int)(rightOperand as ConstantExpression).value); // Compute less than case BinaryOperator.Lt: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value < (int)(rightOperand as ConstantExpression).value); // Compute less than or equal to case BinaryOperator.Leq: // Cast values to int, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (int)(leftOperand as ConstantExpression).value <= (int)(rightOperand as ConstantExpression).value); // Compute addition case BinaryOperator.Add: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value + (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute subtraction case BinaryOperator.Sub: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value - (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute multiplication case BinaryOperator.Mul: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value * (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute division case BinaryOperator.Div: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value / (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); // Compute modulo case BinaryOperator.Rem: // Cast values to int, apply operation and create constant expression return new ConstantExpression(Primitive.Int, ((int)(leftOperand as ConstantExpression).value % (int)(rightOperand as ConstantExpression).value).ToString(NumberFormatInfo.InvariantInfo)); } } // If type is string else if (leftOperand.returnType.Equals(PrimitiveType.STRING)) { switch (op) { // Compute equality case BinaryOperator.Eq: // Convert values to string, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (leftOperand as ConstantExpression).value.ToString() == (rightOperand as ConstantExpression).value.ToString()); // Compute non-equality case BinaryOperator.Neq: // Convert values to string, apply operation and create constant (boolean) expression return new ConstantExpression(Primitive.Bool, (leftOperand as ConstantExpression).value.ToString() != (rightOperand as ConstantExpression).value.ToString()); // Compute addition case BinaryOperator.Add: // Convert values to string, apply operation and create constant expression return new ConstantExpression(Primitive.String, (leftOperand as ConstantExpression).value.ToString() + (rightOperand as ConstantExpression).value.ToString()); } } } return this; }
/// <summary> /// Emits code for this expression /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> public override void EmitCode(ILGenerator ilGen, Scope scope) { // Variable references are emitted by the symbol table because different variables are referenced with // different instructions and only the symbol table knows how to correctly refer them scope.EmitVariableReference(name, ilGen); }
/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public override Expression Evaluate(Scope scope) { // Evaluate operand expression operand = operand.Evaluate(scope); // Save operand return type PrimitiveType pType = (PrimitiveType)operand.returnType; if (pType == null) { return null; } // Processing is done depending on operator switch (op) { // Process unary minus case UnaryOperator.UMinus: // If operand is integer or double if (pType.type == Primitive.Int || pType.type == Primitive.Double) { // Set return type this.returnType = new PrimitiveType(pType.type); // If operand is constant perform folding if (operand is ConstantExpression) { // Fold integer value if (pType.type == Primitive.Int) { // Cast value to int, add unary minus and create new constant expression return new ConstantExpression(Primitive.Int, (-(int)(operand as ConstantExpression).value).ToString()); } else if (pType.type == Primitive.Double) { // Cast value to double, add unary minus and create new constant expression return new ConstantExpression(Primitive.Double, (-(double)(operand as ConstantExpression).value).ToString()); } } } else { // Unary minus cannot be applied on other types this.returnType = PrimitiveType.UNSUPPORTED; // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Can only apply '-' to numeric types"); return this; } break; // Process logical negation case UnaryOperator.Not: // If operand is boolean if (pType.type == Primitive.Bool) { // Set return type this.returnType = new PrimitiveType(pType.type); // If oprand is constant perform folding if (operand is ConstantExpression) { // Cast value to bool, negate and create new constant expression return new ConstantExpression(Primitive.Bool, (bool)(operand as ConstantExpression).value ? "false" : "true"); } } else { // Logical negation cannot be applied on other types this.returnType = PrimitiveType.UNSUPPORTED; // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Can only apply '!' to boolean types"); return this; } break; } return this; }
/// <summary> /// Creates a new ProgramScope /// </summary> /// <param name="parentScope">Parent scope (GlobalScope object should be used)</param> public ProgramScope(Scope parentScope) { // Initialize private members functions = new List<FunctionDeclaration>(); variables = new List<VariableDeclaration>(); this.parentScope = parentScope; }
/// <summary> /// Creates a new LocalScope given a parent scope /// </summary> /// <param name="parentScope">Parent scope</param> public LocalScope(Scope parentScope) { // Initialize private members variables = new List<VariableDeclaration>(); arguments = new List<VariableDeclaration>(); this.parentScope = parentScope; }
/// <summary> /// Creates a new FunctionDeclaration given a function name and a parent scope /// </summary> /// <param name="name">Function name</param> /// <param name="parentScope">Parent scope of this function</param> public FunctionDeclaration(string name, Scope parentScope) : this(parentScope) { this.name = name; }
/// <summary> /// Evaluates this node and all of its children /// </summary> /// <param name="scope">The scope of this statement</param> /// <returns></returns> public override Statement Evaluate(Scope scope) { // Evaluate condition condition = condition.Evaluate(scope); // Evaluate repetitive statement body = body.Evaluate(scope); // Perform dead code elimination if the value of the condition can be evaluated at compile time if (condition is ConstantExpression) { // If condition is always false, drop statement if (!(condition as ConstantExpression).IsTrue()) return null; } return this; }
/// <summary> /// Emits code for this statement /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this statement</param> public override void EmitCode(ILGenerator ilGen, Scope scope) { // Define labels Label loopLabel = ilGen.DefineLabel(); Label endLabel = ilGen.DefineLabel(); // Mark the start of the loop ilGen.MarkLabel(loopLabel); // Emit code for the condition condition.EmitCode(ilGen, scope); // If the value on top of the stack evaluates to 0, jump to the end of the statement ilGen.Emit(OpCodes.Brfalse, endLabel); // Emit code for the repetitive statement body.EmitCode(ilGen, scope); // Jump to the beginning of the statement ilGen.Emit(OpCodes.Br, loopLabel); // Mark end label ilGen.MarkLabel(endLabel); }
/// <summary> /// Emits code for indexers /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> private void EmitIndexers(ILGenerator ilGen, Scope scope) { // Emit code for the variable reference operand.EmitCode(ilGen, scope); // Emit code for each indexer foreach (Expression indexer in indexers) { indexer.EmitCode(ilGen, scope); } }
/// <summary> /// Emits code for this expression /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> public override void EmitCode(ILGenerator ilGen, Scope scope) { // Emit code for operand operand.EmitCode(ilGen, scope); // Emitted code depends on operator switch (op) { // Emit code for unary minus case UnaryOperator.UMinus: // Call neg ilGen.Emit(OpCodes.Neg); break; // Emit code for logical negation case UnaryOperator.Not: // Logical negation is done by comparing value with 0 - opcode not creates a bitwise complement, not a // negation ilGen.Emit(OpCodes.Ldc_I4_0); ilGen.Emit(OpCodes.Ceq); break; } }
/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public override Expression Evaluate(Scope scope) { // Return type is set in constructor // No changes needed return this; }
/// <summary> /// Emits code for an assignement given the right-side expression of the assignement /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> /// <param name="rightSide">Right-side expression</param> public override void EmitAssignement(ILGenerator ilGen, Scope scope, Expression rightSide) { // Emit code for right side expression rightSide.EmitCode(ilGen, scope); // Use symbol table to emit variable assignement scope.EmitVariableAssignement(name, ilGen); }
/// <summary> /// Emits code for this expression /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> public override void EmitCode(ILGenerator ilGen, Scope scope) { // If operator is arithmetical or a comparison (not logical) if ((int)op < 12) { // Emit code for operands leftOperand.EmitCode(ilGen, scope); rightOperand.EmitCode(ilGen, scope); // Code is emitted depending on operation switch (op) { // Arithmetic operators // Emit addition case BinaryOperator.Add: // Check if type is string if (leftOperand.returnType.Equals(PrimitiveType.STRING)) { // For strings, call method System.String.Concat(string, string) ilGen.Emit(OpCodes.Call, Type.GetType("System.String").GetMethod("Concat", new Type[] { typeof(string), typeof(string) })); } else { // For numerical types, emit add ilGen.Emit(OpCodes.Add); } break; // Emit subtraction case BinaryOperator.Sub: ilGen.Emit(OpCodes.Sub); break; // Emit multiplication case BinaryOperator.Mul: ilGen.Emit(OpCodes.Mul); break; // Emit division case BinaryOperator.Div: ilGen.Emit(OpCodes.Div); break; // Emit modulo case BinaryOperator.Rem: ilGen.Emit(OpCodes.Rem); break; // Comparison operators // Emit equality case BinaryOperator.Eq: ilGen.Emit(OpCodes.Ceq); break; // Emit non-equality case BinaryOperator.Neq: // Neq, leg and geq are simulated by negating eq, gt and lt respectively // Emit equality check ilGen.Emit(OpCodes.Ceq); // Load 0 onto the stack ilGen.Emit(OpCodes.Ldc_I4_0); // Check equality ilGen.Emit(OpCodes.Ceq); break; // Emit greater than case BinaryOperator.Gt: ilGen.Emit(OpCodes.Cgt); break; // Emit less than case BinaryOperator.Lt: ilGen.Emit(OpCodes.Clt); break; // Emit less than or equal to case BinaryOperator.Leq: // Emit greater than check ilGen.Emit(OpCodes.Cgt); // Load 0 onto the stack ilGen.Emit(OpCodes.Ldc_I4_0); // Check equality ilGen.Emit(OpCodes.Ceq); break; // Emit greater than or equal to case BinaryOperator.Geq: // Emit less than check ilGen.Emit(OpCodes.Clt); // Load 0 onto the stack ilGen.Emit(OpCodes.Ldc_I4_0); // Check equality ilGen.Emit(OpCodes.Ceq); break; } } // Operator is logical - logical operators are emitted following the rules: // for and: if first operand is false, second operand is not evaluated and expression is considered false // for or: if first operand is true, second operand is not vealuated and expression is considered true else { // Labels needed to skip evaluations if necessary Label falseLabel; Label trueLabel; Label endLabel; // Code is emitted depending on operator switch (op) { // Emit logical and case BinaryOperator.And: // Define labels falseLabel = ilGen.DefineLabel(); endLabel = ilGen.DefineLabel(); // Emit code for left operand leftOperand.EmitCode(ilGen, scope); // Brake to false if first operand is false ilGen.Emit(OpCodes.Brfalse, falseLabel); // Emit code for right operand rightOperand.EmitCode(ilGen, scope); // Break to end ilGen.Emit(OpCodes.Br, endLabel); // Mark false label ilGen.MarkLabel(falseLabel); // Push 0 onto the stack - brfalse pops value from the stack while br doesn't ilGen.Emit(OpCodes.Ldc_I4_0); // Mark end label ilGen.MarkLabel(endLabel); break; // Emit logical or case BinaryOperator.Or: // Define labels trueLabel = ilGen.DefineLabel(); endLabel = ilGen.DefineLabel(); // Emit code for left operand leftOperand.EmitCode(ilGen, scope); // Break to true if first operand is true ilGen.Emit(OpCodes.Brtrue, trueLabel); // Emit code for right operand rightOperand.EmitCode(ilGen, scope); // Break to end ilGen.Emit(OpCodes.Br, endLabel); // Mark true label ilGen.MarkLabel(trueLabel); // Push 1 onto the stack - brtrue pops value from the stack while br doesn't ilGen.Emit(OpCodes.Ldc_I4_1); // Mark end label ilGen.MarkLabel(endLabel); break; // Emit logical xor case BinaryOperator.Xor: // Emit code for operands leftOperand.EmitCode(ilGen, scope); rightOperand.EmitCode(ilGen, scope); // Emit xor ilGen.Emit(OpCodes.Xor); break; } } }
/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public override Expression Evaluate(Scope scope) { // Set return type by getting variable from symbol table returnType = scope.GetVariable(name); // Check if no type was returned if (returnType == null) { // Issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Unknown variable reference"); } return this; }
/// <summary> /// Emits code for this expression /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> public abstract void EmitCode(ILGenerator ilGen, Scope scope);
/// <summary> /// Emits code for this expression /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> public override void EmitCode(ILGenerator ilGen, Scope scope) { // Emit code for all arguments foreach (Expression expr in arguments) { expr.EmitCode(ilGen, scope); } // Get metadata token for method from symbol table and emit call ilGen.Emit(OpCodes.Call, scope.GetMethodInfo(this)); }
/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public abstract Expression Evaluate(Scope scope);
/// <summary> /// Emits code for an assignement given the right-side expression of the assignement /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> /// <param name="rightSide">Right-side expression</param> public abstract void EmitAssignement(ILGenerator ilGen, Scope scope, Expression rightSide);
/// <summary> /// Emits code for assignement /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> /// <param name="rightSide">Right-side expression</param> public override void EmitAssignement(ILGenerator ilGen, Scope scope, Expression rightSide) { // Emit code to index array EmitIndexers(ilGen, scope); // Emit code for right-side expression rightSide.EmitCode(ilGen, scope); // Call Set to store value at indexed position ilGen.Emit(OpCodes.Call, ((ArrayType)operand.returnType).ToCLRType().GetMethod("Set")); }
/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public override Expression Evaluate(Scope scope) { // Evaluate operand operand = operand.Evaluate(scope); // If types are actually equal if (operand.returnType.Equals(this.returnType)) { // Issue warning Compiler.Compiler.errors.Warning(t.line, t.col, "Typecast to the same type"); // Drop the typecast expression return operand; } // If an implicit type cast exists, no need to evaluate further if (operand.returnType.IsCompatible(this.returnType)) { if (returnType.Equals(PrimitiveType.DOUBLE)) { // If operand is constant, fold typecast if (operand is ConstantExpression) { // Cast value to int, convert to double and create new constant expression return new ConstantExpression(Primitive.Double, Convert.ToDouble((int)(operand as ConstantExpression).value)); } return this; } } // Only explicit typecast implemented is from Double to Int if (operand.returnType.Equals(PrimitiveType.DOUBLE)) { if (returnType.Equals(PrimitiveType.INT)) { // If operand is constant, fold typecast if (operand is ConstantExpression) { // Cast value to double, convert to int and create new constant expression return new ConstantExpression(Primitive.Int, Convert.ToInt32((double)(operand as ConstantExpression).value)); } return this; } } // Execution shouldn't reach this line if cast is correct - issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Invalid typecast"); return this; }
/// <summary> /// Emits code for this expression /// </summary> /// <param name="ilGen">IL generator object</param> /// <param name="scope">The scope of this expression</param> public override void EmitCode(ILGenerator ilGen, Scope scope) { // Emit code to index array EmitIndexers(ilGen, scope); // Call Get to retrieve value at indexed position ilGen.Emit(OpCodes.Call, ((ArrayType)operand.returnType).ToCLRType().GetMethod("Get")); }
void FunctionDeclaration(out FunctionDeclaration decl, Scope parentScope) { Statement stmt; BaseType type; VariableDeclarationList decls; Expect(6); Expect(1); decl = new FunctionDeclaration(t, parentScope); Expect(38); if (StartOf(1)) { Type(out type); Expect(1); decl.AddArgument(t, type); while (la.kind == 31) { Get(); Type(out type); Expect(1); decl.AddArgument(t, type); } } Expect(45); if (la.kind == 30) { Get(); Type(out type); decl.returnType = type; } while (la.kind == 14) { VariableDeclarations(out decls); decl.localScope.AddVariables(decls); } BlockStatement(out stmt); decl.body = stmt as BlockStatement; }
/// <summary> /// Evaluates this node and all its children /// </summary> /// <param name="scope">The scope of this expression</param> /// <returns></returns> public override Expression Evaluate(Scope scope) { // Iterate through indexers for (int i = 0; i < indexers.Count; i++) { // Evaluate indexer indexers[i] = indexers[i].Evaluate(scope); // Check if indexer is of integer type if (!indexers[i].returnType.Equals(PrimitiveType.INT)) { // Ilegal indexer type - issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Indexers must be of integer type"); return this; } } // Evaluate operand expression operand = operand.Evaluate(scope); if (operand is IndexerExpression) { Compiler.Compiler.errors.SemErr(operand.t.line, operand.t.col, "Cannot apply multiple indexers on array. Use [,] instead of [][]"); return this; } // Try to cast operand return type to ArrayType ArrayType refType = operand.returnType as ArrayType; // Check if cast was successful if (refType == null) { // Indexers can only be applied to array types - issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Cannot apply indexers to non-array type"); return this; } // Check if the dimension of the array is equal to the number of indexers if (refType.dimensions != indexers.Count) { // Cannot assign to arrays, only to values - issue error Compiler.Compiler.errors.SemErr(t.line, t.col, "Invalid number of indexers"); return this; } // Set return type this.returnType = refType.type; return this; }