protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { bool evaluateForSideEffectsOnly = false; switch (purpose) { case EvaluationIntention.SideEffectsOnly: evaluateForSideEffectsOnly = true; break; case EvaluationIntention.Value: case EvaluationIntention.ValueOrNode: break; default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } var what = Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.AddressOrNode); if (what == null) { // it is a temporary (and lvalue), so must be an address if (evaluateForSideEffectsOnly) { // value not needed for other expression // stack has: address context.GenerateInstruction("IDEC"); // stack has: ... } else { // stack has: address context.GenerateInstruction("DUP"); // copy the address // stack has: address, address context.GenerateInstruction("Indirection"); // stack has: address, value context.GenerateInstruction("SWAP"); // stack has: value, address context.GenerateInstruction("IDEC"); // stack has: value } } else if (what is VariableTreeNode variable) { context.GenerateInstruction("PUSH", variable.Value.ToString( )); if (!evaluateForSideEffectsOnly) { context.GenerateInstruction("DUP"); // copy original value to leave on the stack } context.GenerateInstruction("DEC"); context.GenerateInstruction("POP", variable.Value.ToString( )); } else { throw new AssertionFailedException("unexpected target" + what.ToString()); } return(null); }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { // NB: a C string's value is its address. // (There is no notion of a string's address as that would be an address of an address.) switch (purpose) { case EvaluationIntention.Value: case EvaluationIntention.ValueOrNode: // might be a case of *(p+i), which can be done as p[i]; if (Arg is AddressOfTreeNode addrOf1) { // 3404 // 3401: collapse *&(expr) on RHS and just generate code for the underlying expr context.SetPrettyPrintProlog("<skipped> "); context.PrettyPrint(Arg); return(addrOf1.Arg.GenerateCodeForValueWithPrettyPrint(context, purpose)); } else { if (Arg is AdditionTreeNode addition) { context.SetPrettyPrintProlog("<skipped> "); context.PrettyPrint(Arg); addition.Left.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); addition.Right.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); context.GenerateInstruction("Subscript"); } else { Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); context.GenerateInstruction("Indirection"); } } break; case EvaluationIntention.SideEffectsOnly: Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.SideEffectsOnly); break; case EvaluationIntention.AddressOrNode: if (Arg is AddressOfTreeNode addrOf2) { // 3401: collapse *&(expr) on LHS and just generate code for the underlying expr context.SetPrettyPrintProlog("<skipped> "); context.PrettyPrint(Arg); return(addrOf2.Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.AddressOrNode)); } else { // NB: if we did ValueOrNode here, we'd have to handle Node results specially // as we cannot allow a Node from ValueOrNode unmodified as AddressOrNode Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); } break; default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } return(null); }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { bool so = false; switch (purpose) { case EvaluationIntention.SideEffectsOnly: so = true; break; case EvaluationIntention.Value: case EvaluationIntention.ValueOrNode: break; default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } var secondChoice = context.CreateLabel(); Pre.GenerateCodeForConditionalBranchWithPrettyPrint(context, secondChoice, false); Mid.GenerateCodeForValueWithPrettyPrint(context, so ? EvaluationIntention.SideEffectsOnly : EvaluationIntention.Value); var joinPoint = context.CreateLabel(); context.GenerateUnconditionalBranch(joinPoint); context.PlaceLabelHere(secondChoice); Post.GenerateCodeForValueWithPrettyPrint(context, so ? EvaluationIntention.SideEffectsOnly : EvaluationIntention.Value); context.PlaceLabelHere(joinPoint); return(null); }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { // NB: a C string's value is its address. (There is no notion of a string's address.) switch (purpose) { case EvaluationIntention.Value: context.GenerateInstruction("PUSH", string.Format("\"{0}\"", Value)); break; case EvaluationIntention.SideEffectsOnly: break; default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } return(null); }
// NB: Short Circut Evaluation (Left || Right): if "Left" evaluates to true we must not evaluate "Right" protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { switch (purpose) { case EvaluationIntention.SideEffectsOnly: // We have something like an expression statement: // a || b; // or perhaps a parameter as in // f(a||b); // This requires differentiated evaluation for left/first and right/second as follows: // a's truth value determines whether b is executed or skipped (short-circuted). // if a is false, then b is executed; otherwise if a is true, b is skipped. // thus, we need to evaluate "a" for conditional branch, and to branch around "b"! // however, b does not affect anything further (and we don't need a final value for (a||b) either). // Thus, b is evaluated for side effects only. var joinPoint1 = context.CreateLabel(); Left.GenerateCodeForConditionalBranchWithPrettyPrint(context, joinPoint1, true); Right.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.SideEffectsOnly); context.PlaceLabelHere(joinPoint1); return(null); case EvaluationIntention.Value: case EvaluationIntention.ValueOrNode: // we have an expression like (a || b) + 3 so we treat that like: ((a || b) ? 1 : 0) + 3; var zero = context.CreateLabel(); context.EvalEither(Left, Right, zero, false); // get temp, so easy on stack machine context.GenerateInstruction("PUSH", "#1"); var joinPoint2 = context.CreateLabel(); context.GenerateUnconditionalBranch(joinPoint2); context.PlaceLabelHere(zero); context.GenerateInstruction("PUSH", "#0"); context.PlaceLabelHere(joinPoint2); return(null); default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } }
// This on is to be invoked; it performs pretty printing, and then invokes the protected (real/overridden) one. public AbstractSyntaxTree GenerateCodeForValueWithPrettyPrint(CodeGenContext context, EvaluationIntention purpose) { context.PrettyPrint(this); return(GenerateCodeForValue(context, purpose)); }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { switch (purpose) { case EvaluationIntention.Value: context.GenerateInstruction("PUSH", Value.ToString()); break; case EvaluationIntention.ValueOrNode: return(this); case EvaluationIntention.SideEffectsOnly: break; case EvaluationIntention.AddressOrNode: return(this); default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } return(null); }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { switch (purpose) { case EvaluationIntention.SideEffectsOnly: Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.SideEffectsOnly); return(null); case EvaluationIntention.Value: case EvaluationIntention.ValueOrNode: Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); if (Op != Operators.Operator.FixPoint) { context.GenerateInstruction(Op.ToString()); } return(null); // case EvaluationIntention.AddressOrNode: // Arg.GenerateCodeForValueWithPrettyPrint ( context, EvaluationIntention.Value ); // return null; default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } }
/// <summary> /// Generates code for the given tree node. /// </summary> /// <param name="context"> /// where the generated code is output to, and some helper functions /// </param> /// <param name="purpose"> /// A tree can be evalutated for code generation: /// for side effects only: /// used to evaluate statements that discard results /// expect null for return value /// for the value produced by the expression (unconditionally): /// used to evaluate most operators in an expression context /// expect null for return value meaning the result is in the "temporary" location /// for the value produced by the expression when complex, or the node when a simple variable /// used to evaluate function expressions /// expect null meaning the result is in the "temporary" location, or, /// a variable if the expression evaluates to a simple variable /// for the address of the expression when complex, or the node when a simple variable /// used to evaluate left-hand-side of assignment operators /// expect null when the result is in the "temporary" location, or, /// variable if the result is the address of a variable /// Note: a tree can also be evalutated for conditional branch, but that is done with GenerateCodeForConditionalBranch, below. /// See EvaluationIntention for more details. /// </param> /// <returns> /// The return value takes on meaning in the context of the intended purpose of evaluation. /// It is a tree node that captures the mode of the loaded value: /// null -- indicating the expression result is a "temporary", or is nothing /// variable -- if the temporary result is a variable or address /// </returns> /// This is meant to be overridden by subclasses, but not called; hence protected! protected abstract AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose);
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { Arg.GenerateCodeForValueWithPrettyPrint(context, purpose); switch (purpose) { case EvaluationIntention.SideEffectsOnly: Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.SideEffectsOnly); return(null); case EvaluationIntention.Value: case EvaluationIntention.ValueOrNode: Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); context.GenerateInstruction("NEG"); return(null); default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { // To take the address of a child expression, we capitalize on having an evaluation intention mode that matches this. // We have this mode to support assignment and address of operations. // Note that we must have a notion of evaluating an expression for its location rather than for its value, as // we could not evaluate the left hand side of an assignment for value, and then take its address -- that doesn't work! // By using the address evaluation intention for the child, there is then sometimes no code to generate for this operator. // When it does need to generate code, the case of &a, for example, the it uses the PEA instruction // Also note that this approach automatically detects and optimizes a sequence like &* // // Contrast this above with the Indirection operator // The other operator, Indirection, needs to special case *& to collapse these two. // This is because we don't have an evaluation intention mode for indirection, // Instead we generate the indirection's value and the use an Indirection instruction to dereference it. // switch (purpose) { case EvaluationIntention.SideEffectsOnly: Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.SideEffectsOnly); break; case EvaluationIntention.Value: var ans = Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.AddressOrNode); if (ans != null) { if (ans is VariableTreeNode variable) { // PEA == Push Effective Address context.GenerateInstruction("PEA", variable.Value.ToString()); } else { throw new AssertionFailedException("unexpected result for &"); } } break; case EvaluationIntention.ValueOrNode: return(Arg.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.AddressOrNode)); default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } return(null); }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { switch (purpose) { case EvaluationIntention.SideEffectsOnly: Left.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.SideEffectsOnly); Right.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.SideEffectsOnly); return(null); case EvaluationIntention.Value: case EvaluationIntention.ValueOrNode: Left.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); Right.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); context.GenerateInstruction(Op.ToString()); return(null); case EvaluationIntention.AddressOrNode: Left.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); Right.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); context.GenerateInstruction("AddIndex"); return(null); default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { var functionToCall = Left.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.ValueOrNode); // NB: Function calls are binary operators in the sense that they have two arguments. // The first argument is the function to call, and the second is: // possibly null -- if no arguments are supplied, or, // a single parameter -- if one argument is supplied, or, // a tree of ArgumentSeparator, whose left is // a single parameter, or, // a tree of ArugmentSeparator ... // and whose Right is a single parameter. var argCount = context.EvaluateArgumentList(Right); if (functionToCall == null) { context.GenerateInstruction("ICALL", string.Format("#{0}", argCount)); } else if (functionToCall is VariableTreeNode variable) { context.GenerateInstruction("CALL", variable.Value.ToString(), string.Format("#{0}", argCount)); } else { throw new AssertionFailedException("unknown function"); } if (purpose == EvaluationIntention.SideEffectsOnly) { context.GenerateInstruction("POP"); } return(null); }
protected override AbstractSyntaxTree GenerateCodeForValue(CodeGenContext context, EvaluationIntention purpose) { // When we ask for AddressOrNode, we will get either an address // e.g. for a[i] += ..., we'll get a+i on the stack, or, // for i += ..., we'll get nothing on the stack, so we can pop directly into i var target = Left.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.AddressOrNode); var variable = target as VariableTreeNode; if (Op != Assignment) { GenerateLHSValue(context, target, variable); } Right.GenerateCodeForValueWithPrettyPrint(context, EvaluationIntention.Value); var keep = false; switch (purpose) { case EvaluationIntention.Value: case EvaluationIntention.ValueOrNode: keep = true; break; case EvaluationIntention.SideEffectsOnly: break; default: throw new AssertionFailedException("unexpected evaluation intention" + purpose); } if (Op != Assignment) { GenerateAssignmentComputation(context); } if (target == null) { // this case is that we generated an address onto the stack for the target // so, we need to use indirection operators if (keep) { // Stack has LeftY | RightTop <-- stack top // This instruction does *LeftY = RightTop // and pops only Left off the stack, leaving Right context.GenerateInstruction("ISTORE"); } else { // Stack has LeftY | RightTop <-- stack top // This instruction does *LeftYT = RightTop // and pops both Left and Right off the stack context.GenerateInstruction("IPOP"); } } else { // this case is that we generated nothing onto the stack for the left hand side // so, we'll pop or store directly into if (keep) { // Stack has RightTop <-- stack top // This instruction does var = RightTop // and does not pop Right // This form is used when the assignment is used // as in f(a=b); in which b is assigned into a, and the value is passed to f context.GenerateInstruction("STORE", variable.Value.ToString()); } else { // Stack has RightTop <-- stack top // This instruction does var = RightTop // and does pop Right off the stack, because the value is not wanted. context.GenerateInstruction("POP", variable.Value.ToString()); } } return(null); }