JUnaryExpression.java |
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