1   // Copyright 2013 Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import static jminusminus.CLConstants.*;
6   
7   /**
8    * The AST node for a unary expression. A unary expression has a single operand.
9    */
10  
11  abstract class JUnaryExpression extends JExpression {
12  
13      /** The operator. */
14      private String operator;
15  
16      /** The operand. */
17      protected JExpression arg;
18  
19      /**
20       * Construct an AST node for a unary expression given its line number, the
21       * unary operator, and the operand.
22       * 
23       * @param line
24       *            line in which the unary expression occurs in the source file.
25       * @param operator
26       *            the unary operator.
27       * @param arg
28       *            the operand.
29       */
30  
31      protected JUnaryExpression(int line, String operator, JExpression arg) {
32          super(line);
33          this.operator = operator;
34          this.arg = arg;
35      }
36  
37      /**
38       * @inheritDoc
39       */
40  
41      public void writeToStdOut(PrettyPrinter p) {
42          p.printf("<JUnaryExpression line=\"%d\" type=\"%s\" "
43                  + "operator=\"%s\">\n", line(), ((type == null) ? "" : type
44                  .toString()), Util.escapeSpecialXMLChars(operator));
45          p.indentRight();
46          p.printf("<Operand>\n");
47          p.indentRight();
48          arg.writeToStdOut(p);
49          p.indentLeft();
50          p.printf("</Operand>\n");
51          p.indentLeft();
52          p.printf("</JUnaryExpression>\n");
53      }
54  
55  }
56  
57  /**
58   * The AST node for a unary negation (-) expression.
59   */
60  
61  class JNegateOp extends JUnaryExpression {
62  
63      /**
64       * Construct an AST node for a negation expression given its line number,
65       * and the operand.
66       * 
67       * @param line
68       *            line in which the negation expression occurs in the source
69       *            file.
70       * @param arg
71       *            the operand.
72       */
73  
74      public JNegateOp(int line, JExpression arg) {
75          super(line, "-", arg);
76      }
77  
78      /**
79       * Analyzing the negation operation involves analyzing its operand, checking
80       * its type and determining the result type.
81       * 
82       * @param context
83       *            context in which names are resolved.
84       * @return the analyzed (and possibly rewritten) AST subtree.
85       */
86  
87      public JExpression analyze(Context context) {
88          arg = arg.analyze(context);
89          arg.type().mustMatchExpected(line(), Type.INT);
90          type = Type.INT;
91          return this;
92      }
93  
94      /**
95       * Generating code for the negation operation involves generating code for
96       * the operand, and then the negation instruction.
97       * 
98       * @param output
99       *            the code emitter (basically an abstraction for producing the
100      *            .class file).
101      */
102 
103     public void codegen(CLEmitter output) {
104         arg.codegen(output);
105         output.addNoArgInstruction(INEG);
106     }
107 
108 }
109 
110 /**
111  * The AST node for a logical NOT (!) expression.
112  */
113 
114 class JLogicalNotOp extends JUnaryExpression {
115 
116     /**
117      * Construct an AST for a logical NOT expression given its line number, and
118      * the operand.
119      * 
120      * @param line
121      *            line in which the logical NOT expression occurs in the source
122      *            file.
123      * @param arg
124      *            the operand.
125      */
126 
127     public JLogicalNotOp(int line, JExpression arg) {
128         super(line, "!", arg);
129     }
130 
131     /**
132      * Analyzing a logical NOT operation means analyzing its operand, insuring
133      * it's a boolean, and setting the result to boolean.
134      * 
135      * @param context
136      *            context in which names are resolved.
137      */
138 
139     public JExpression analyze(Context context) {
140         arg = (JExpression) arg.analyze(context);
141         arg.type().mustMatchExpected(line(), Type.BOOLEAN);
142         type = Type.BOOLEAN;
143         return this;
144     }
145 
146     /**
147      * Generate code for the case where we actually want a boolean value (true
148      * or false) computed onto the stack, eg for assignment to a boolean
149      * variable.
150      * 
151      * @param output
152      *            the code emitter (basically an abstraction for producing the
153      *            .class file).
154      */
155 
156     public void codegen(CLEmitter output) {
157         String elseLabel = output.createLabel();
158         String endIfLabel = output.createLabel();
159         this.codegen(output, elseLabel, false);
160         output.addNoArgInstruction(ICONST_1); // true
161         output.addBranchInstruction(GOTO, endIfLabel);
162         output.addLabel(elseLabel);
163         output.addNoArgInstruction(ICONST_0); // false
164         output.addLabel(endIfLabel);
165     }
166 
167     /**
168      * The code generation necessary for branching simply flips the condition on
169      * which we branch.
170      * 
171      * @param output
172      *            the code emitter (basically an abstraction for producing the
173      *            .class file).
174      */
175 
176     public void codegen(CLEmitter output, String targetLabel, boolean onTrue) {
177         arg.codegen(output, targetLabel, !onTrue);
178     }
179 
180 }
181 
182 /**
183  * The AST node for an expr--.
184  */
185 
186 class JPostDecrementOp extends JUnaryExpression {
187 
188     /**
189      * Construct an AST node for an expr-- expression given its line number, and
190      * the operand.
191      * 
192      * @param line
193      *            line in which the expression occurs in the source file.
194      * @param arg
195      *            the operand.
196      */
197 
198     public JPostDecrementOp(int line, JExpression arg) {
199         super(line, "post--", arg);
200     }
201 
202     /**
203      * Analyze the operand as a lhs (since there is a side effect), check types
204      * and determine the type of the result.
205      * 
206      * @param context
207      *            context in which names are resolved.
208      * @return the analyzed (and possibly rewritten) AST subtree.
209      */
210 
211     public JExpression analyze(Context context) {
212         if (!(arg instanceof JLhs)) {
213             JAST.compilationUnit.reportSemanticError(line,
214                     "Operand to expr-- must have an LValue.");
215             type = Type.ANY;
216         } else {
217             arg = (JExpression) arg.analyze(context);
218             arg.type().mustMatchExpected(line(), Type.INT);
219             type = Type.INT;
220         }
221         return this;
222     }
223 
224     /**
225      * In generating code for a post-decrement operation, we treat simple
226      * variable (JVariable) operands specially since the JVM has an increment
227      * instruction. Otherwise, we rely on the JLhs code generation support for
228      * generating the proper code. Notice that we distinguish between
229      * expressions that are statement expressions and those that are not; we
230      * insure the proper value (before the decrement) is left atop the stack in
231      * the latter case.
232      * 
233      * @param output
234      *            the code emitter (basically an abstraction for producing the
235      *            .class file).
236      */
237 
238     public void codegen(CLEmitter output) {
239         if (arg instanceof JVariable) {
240             // A local variable; otherwise analyze() would
241             // have replaced it with an explicit field selection.
242             int offset = ((LocalVariableDefn) ((JVariable) arg).iDefn())
243                     .offset();
244             if (!isStatementExpression) {
245                 // Loading its original rvalue
246                 arg.codegen(output);
247             }
248             output.addIINCInstruction(offset, -1);
249         } else {
250             ((JLhs) arg).codegenLoadLhsLvalue(output);
251             ((JLhs) arg).codegenLoadLhsRvalue(output);
252             if (!isStatementExpression) {
253                 // Loading its original rvalue
254                 ((JLhs) arg).codegenDuplicateRvalue(output);
255             }
256             output.addNoArgInstruction(ICONST_1);
257             output.addNoArgInstruction(ISUB);
258             ((JLhs) arg).codegenStore(output);
259         }
260     }
261 
262 }
263 
264 /**
265  * The AST node for a ++expr expression.
266  */
267 
268 class JPreIncrementOp extends JUnaryExpression {
269 
270     /**
271      * Construct an AST node for a ++expr given its line number, and the
272      * operand.
273      * 
274      * @param line
275      *            line in which the expression occurs in the source file.
276      * @param arg
277      *            the operand.
278      */
279 
280     public JPreIncrementOp(int line, JExpression arg) {
281         super(line, "++pre", arg);
282     }
283 
284     /**
285      * Analyze the operand as a lhs (since there is a side effect), check types
286      * and determine the type of the result.
287      * 
288      * @param context
289      *            context in which names are resolved.
290      * @return the analyzed (and possibly rewritten) AST subtree.
291      */
292 
293     public JExpression analyze(Context context) {
294         if (!(arg instanceof JLhs)) {
295             JAST.compilationUnit.reportSemanticError(line,
296                     "Operand to ++expr must have an LValue.");
297             type = Type.ANY;
298         } else {
299             arg = (JExpression) arg.analyze(context);
300             arg.type().mustMatchExpected(line(), Type.INT);
301             type = Type.INT;
302         }
303         return this;
304     }
305 
306     /**
307      * In generating code for a pre-increment operation, we treat simple
308      * variable (JVariable) operands specially since the JVM has an increment
309      * instruction. Otherwise, we rely on the JLhs code generation support for
310      * generating the proper code. Notice that we distinguish between
311      * expressions that are statement expressions and those that are not; we
312      * insure the proper value (after the increment) is left atop the stack in
313      * the latter case.
314      * 
315      * @param output
316      *            the code emitter (basically an abstraction for producing the
317      *            .class file).
318      */
319 
320     public void codegen(CLEmitter output) {
321         if (arg instanceof JVariable) {
322             // A local variable; otherwise analyze() would
323             // have replaced it with an explicit field selection.
324             int offset = ((LocalVariableDefn) ((JVariable) arg).iDefn())
325                     .offset();
326             output.addIINCInstruction(offset, 1);
327             if (!isStatementExpression) {
328                 // Loading its original rvalue
329                 arg.codegen(output);
330             }
331         } else {
332             ((JLhs) arg).codegenLoadLhsLvalue(output);
333             ((JLhs) arg).codegenLoadLhsRvalue(output);
334             output.addNoArgInstruction(ICONST_1);
335             output.addNoArgInstruction(IADD);
336             if (!isStatementExpression) {
337                 // Loading its original rvalue
338                 ((JLhs) arg).codegenDuplicateRvalue(output);
339             }
340             ((JLhs) arg).codegenStore(output);
341         }
342     }
343 
344 }
345