• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.turbine.parse;
18 
19 import static com.google.common.collect.Iterables.getOnlyElement;
20 
21 import com.google.common.collect.ImmutableList;
22 import com.google.errorprone.annotations.CheckReturnValue;
23 import com.google.turbine.diag.TurbineError;
24 import com.google.turbine.diag.TurbineError.ErrorKind;
25 import com.google.turbine.model.Const;
26 import com.google.turbine.model.TurbineConstantTypeKind;
27 import com.google.turbine.tree.Tree;
28 import com.google.turbine.tree.Tree.ClassLiteral;
29 import com.google.turbine.tree.Tree.ClassTy;
30 import com.google.turbine.tree.Tree.Expression;
31 import com.google.turbine.tree.Tree.Ident;
32 import com.google.turbine.tree.TurbineOperatorKind;
33 import java.util.Optional;
34 import org.checkerframework.checker.nullness.qual.Nullable;
35 
36 /** A parser for compile-time constant expressions. */
37 public class ConstExpressionParser {
38 
39   Token token;
40   private int position;
41   private final Lexer lexer;
42 
ConstExpressionParser(Lexer lexer, Token token)43   public ConstExpressionParser(Lexer lexer, Token token) {
44     this.lexer = lexer;
45     this.token = token;
46     this.position = lexer.position();
47   }
48 
operator(Token token)49   private static TurbineOperatorKind operator(Token token) {
50     switch (token) {
51       case ASSIGN:
52         // TODO(cushon): only allow in annotations?
53         return TurbineOperatorKind.ASSIGN;
54       case MULT:
55         return TurbineOperatorKind.MULT;
56       case DIV:
57         return TurbineOperatorKind.DIVIDE;
58       case MOD:
59         return TurbineOperatorKind.MODULO;
60       case PLUS:
61         return TurbineOperatorKind.PLUS;
62       case MINUS:
63         return TurbineOperatorKind.MINUS;
64       case LTLT:
65         return TurbineOperatorKind.SHIFT_LEFT;
66       case GTGT:
67         return TurbineOperatorKind.SHIFT_RIGHT;
68       case GTGTGT:
69         return TurbineOperatorKind.UNSIGNED_SHIFT_RIGHT;
70       case LT:
71         return TurbineOperatorKind.LESS_THAN;
72       case GT:
73         return TurbineOperatorKind.GREATER_THAN;
74       case LTE:
75         return TurbineOperatorKind.LESS_THAN_EQ;
76       case GTE:
77         return TurbineOperatorKind.GREATER_THAN_EQ;
78       case EQ:
79         return TurbineOperatorKind.EQUAL;
80       case NOTEQ:
81         return TurbineOperatorKind.NOT_EQUAL;
82       case AND:
83         return TurbineOperatorKind.BITWISE_AND;
84       case OR:
85         return TurbineOperatorKind.BITWISE_OR;
86       case XOR:
87         return TurbineOperatorKind.BITWISE_XOR;
88       case ANDAND:
89         return TurbineOperatorKind.AND;
90       case OROR:
91         return TurbineOperatorKind.OR;
92       case COND:
93         return TurbineOperatorKind.TERNARY;
94       default:
95         return null;
96     }
97   }
98 
primary(boolean negate)99   private Tree.@Nullable Expression primary(boolean negate) {
100     switch (token) {
101       case INT_LITERAL:
102         return finishLiteral(TurbineConstantTypeKind.INT, negate);
103       case DOUBLE_LITERAL:
104         return finishLiteral(TurbineConstantTypeKind.DOUBLE, negate);
105       case LONG_LITERAL:
106         return finishLiteral(TurbineConstantTypeKind.LONG, negate);
107       case FLOAT_LITERAL:
108         return finishLiteral(TurbineConstantTypeKind.FLOAT, negate);
109       case TRUE:
110         eat();
111         return new Tree.Literal(
112             position, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(true));
113       case FALSE:
114         eat();
115         return new Tree.Literal(
116             position, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(false));
117       case CHAR_LITERAL:
118         return finishLiteral(TurbineConstantTypeKind.CHAR, negate);
119       case STRING_LITERAL:
120         return finishLiteral(TurbineConstantTypeKind.STRING, false);
121       case PLUS:
122         eat();
123         return unaryRest(TurbineOperatorKind.UNARY_PLUS);
124       case MINUS:
125         eat();
126         return unaryRest(TurbineOperatorKind.NEG);
127       case NOT:
128         eat();
129         return unaryRest(TurbineOperatorKind.NOT);
130       case TILDE:
131         eat();
132         return unaryRest(TurbineOperatorKind.BITWISE_COMP);
133       case LPAREN:
134         return maybeCast();
135       case LBRACE:
136         int pos = position;
137         eat();
138         return arrayInitializer(pos);
139       case IDENT:
140         return qualIdent();
141       case BYTE:
142         return primitiveClassLiteral(TurbineConstantTypeKind.BYTE);
143       case CHAR:
144         return primitiveClassLiteral(TurbineConstantTypeKind.CHAR);
145       case DOUBLE:
146         return primitiveClassLiteral(TurbineConstantTypeKind.DOUBLE);
147       case FLOAT:
148         return primitiveClassLiteral(TurbineConstantTypeKind.FLOAT);
149       case INT:
150         return primitiveClassLiteral(TurbineConstantTypeKind.INT);
151       case LONG:
152         return primitiveClassLiteral(TurbineConstantTypeKind.LONG);
153       case SHORT:
154         return primitiveClassLiteral(TurbineConstantTypeKind.SHORT);
155       case BOOLEAN:
156         return primitiveClassLiteral(TurbineConstantTypeKind.BOOLEAN);
157       case VOID:
158         eat();
159         return finishClassLiteral(position, new Tree.VoidTy(position));
160       case AT:
161         return annotation();
162       default:
163         return null;
164     }
165   }
166 
primitiveClassLiteral(TurbineConstantTypeKind type)167   private Expression primitiveClassLiteral(TurbineConstantTypeKind type) {
168     eat();
169     return finishClassLiteral(position, new Tree.PrimTy(position, ImmutableList.of(), type));
170   }
171 
maybeCast()172   private Tree.Expression maybeCast() {
173     eat();
174     switch (token) {
175       case BOOLEAN:
176         eat();
177         return castTail(TurbineConstantTypeKind.BOOLEAN);
178       case BYTE:
179         eat();
180         return castTail(TurbineConstantTypeKind.BYTE);
181       case SHORT:
182         eat();
183         return castTail(TurbineConstantTypeKind.SHORT);
184       case INT:
185         eat();
186         return castTail(TurbineConstantTypeKind.INT);
187       case LONG:
188         eat();
189         return castTail(TurbineConstantTypeKind.LONG);
190       case CHAR:
191         eat();
192         return castTail(TurbineConstantTypeKind.CHAR);
193       case DOUBLE:
194         eat();
195         return castTail(TurbineConstantTypeKind.DOUBLE);
196       case FLOAT:
197         eat();
198         return castTail(TurbineConstantTypeKind.FLOAT);
199       default:
200         return notCast();
201     }
202   }
203 
notCast()204   private Tree.Expression notCast() {
205     Tree.Expression expr = expression(null);
206     if (expr == null) {
207       return null;
208     }
209     if (token != Token.RPAREN) {
210       return null;
211     }
212     eat();
213     if (expr.kind() == Tree.Kind.CONST_VAR_NAME) {
214       Tree.ConstVarName cvar = (Tree.ConstVarName) expr;
215       switch (token) {
216         case INT_LITERAL:
217         case FLOAT_LITERAL:
218         case TRUE:
219         case FALSE:
220         case CHAR_LITERAL:
221         case STRING_LITERAL:
222         case NOT:
223         case TILDE:
224         case IDENT:
225           return new Tree.TypeCast(
226               position, asClassTy(cvar.position(), cvar.name()), primary(false));
227         default:
228           return expr;
229       }
230     } else {
231       return expr;
232     }
233   }
234 
asClassTy(int pos, ImmutableList<Tree.Ident> names)235   private static ClassTy asClassTy(int pos, ImmutableList<Tree.Ident> names) {
236     ClassTy cty = null;
237     for (Tree.Ident bit : names) {
238       cty = new ClassTy(pos, Optional.ofNullable(cty), bit, ImmutableList.of(), ImmutableList.of());
239     }
240     return cty;
241   }
242 
eat()243   private void eat() {
244     token = lexer.next();
245     position = lexer.position();
246   }
247 
arrayInitializer(int pos)248   private Tree.Expression arrayInitializer(int pos) {
249     if (token == Token.RBRACE) {
250       eat();
251       return new Tree.ArrayInit(pos, ImmutableList.<Tree.Expression>of());
252     }
253 
254     ImmutableList.Builder<Tree.Expression> exprs = ImmutableList.builder();
255     OUTER:
256     while (true) {
257       if (token == Token.RBRACE) {
258         eat();
259         break OUTER;
260       }
261       Tree.Expression item = expression(null);
262       if (item == null) {
263         return null;
264       }
265       exprs.add(item);
266       switch (token) {
267         case COMMA:
268           eat();
269           break;
270         case RBRACE:
271           eat();
272           break OUTER;
273         default:
274           return null;
275       }
276     }
277     return new Tree.ArrayInit(pos, exprs.build());
278   }
279 
280   /** Finish hex, decimal, octal, and binary integer literals (see JLS 3.10.1). */
finishLiteral(TurbineConstantTypeKind kind, boolean negate)281   private Tree.Expression finishLiteral(TurbineConstantTypeKind kind, boolean negate) {
282     int pos = position;
283     String text = ident().value();
284     Const.Value value;
285     switch (kind) {
286       case INT:
287         {
288           int radix = 10;
289           if (text.startsWith("0x") || text.startsWith("0X")) {
290             text = text.substring(2);
291             radix = 0x10;
292           } else if (isOctal(text)) {
293             radix = 010;
294           } else if (text.startsWith("0b") || text.startsWith("0B")) {
295             text = text.substring(2);
296             radix = 0b10;
297           }
298           if (negate) {
299             text = "-" + text;
300           }
301           long longValue = parseLong(text, radix);
302           if (radix == 10) {
303             if (longValue != (int) longValue) {
304               throw error(ErrorKind.INVALID_LITERAL, text);
305             }
306           } else {
307             if (Math.abs(longValue) >> 32 != 0) {
308               throw error(ErrorKind.INVALID_LITERAL, text);
309             }
310           }
311           value = new Const.IntValue((int) longValue);
312           break;
313         }
314       case LONG:
315         {
316           int radix = 10;
317           if (text.startsWith("0x") || text.startsWith("0X")) {
318             text = text.substring(2);
319             radix = 0x10;
320           } else if (isOctal(text)) {
321             radix = 010;
322           } else if (text.startsWith("0b") || text.startsWith("0B")) {
323             text = text.substring(2);
324             radix = 0b10;
325           }
326           if (negate) {
327             text = "-" + text;
328           }
329           if (text.endsWith("L") || text.endsWith("l")) {
330             text = text.substring(0, text.length() - 1);
331           }
332           value = new Const.LongValue(parseLong(text, radix));
333           break;
334         }
335       case CHAR:
336         value = new Const.CharValue(text.charAt(0));
337         break;
338       case FLOAT:
339         try {
340           value = new Const.FloatValue(Float.parseFloat(text.replace("_", "")));
341         } catch (NumberFormatException e) {
342           throw error(ErrorKind.INVALID_LITERAL, text);
343         }
344         break;
345       case DOUBLE:
346         try {
347           value = new Const.DoubleValue(Double.parseDouble(text.replace("_", "")));
348         } catch (NumberFormatException e) {
349           throw error(ErrorKind.INVALID_LITERAL, text);
350         }
351         break;
352       case STRING:
353         value = new Const.StringValue(text);
354         break;
355       default:
356         throw new AssertionError(kind);
357     }
358     eat();
359     return new Tree.Literal(pos, kind, value);
360   }
361 
isOctal(String text)362   static boolean isOctal(String text) {
363     if (!text.startsWith("0")) {
364       return false;
365     }
366     if (text.length() <= 1) {
367       return false;
368     }
369     char next = text.charAt(1);
370     return Character.isDigit(next) || next == '_';
371   }
372 
373   /**
374    * Parse the string as a signed long.
375    *
376    * <p>{@link Long#parseLong} doesn't accept {@link Long#MIN_VALUE}.
377    */
parseLong(String text, int radix)378   private long parseLong(String text, int radix) {
379     long r = 0;
380     boolean neg = text.startsWith("-");
381     if (neg) {
382       text = text.substring(1);
383     }
384     for (char c : text.toCharArray()) {
385       int digit;
386       if ('0' <= c && c <= '9') {
387         digit = c - '0';
388       } else if ('a' <= c && c <= 'f') {
389         digit = 10 + (c - 'a');
390       } else if ('A' <= c && c <= 'F') {
391         digit = 10 + (c - 'A');
392       } else if (c == '_') {
393         continue;
394       } else {
395         throw error(ErrorKind.INVALID_LITERAL, text);
396       }
397       r = (r * radix) + digit;
398     }
399     if (neg) {
400       r = -r;
401     }
402     return r;
403   }
404 
unaryRest(TurbineOperatorKind op)405   private Tree.Expression unaryRest(TurbineOperatorKind op) {
406     boolean negate = op == TurbineOperatorKind.NEG;
407     Tree.Expression expr = primary(negate);
408     if (expr == null) {
409       return null;
410     }
411     if (negate && expr.kind() == Tree.Kind.LITERAL) {
412       Tree.Literal lit = (Tree.Literal) expr;
413       switch (lit.tykind()) {
414         case INT:
415         case LONG:
416           return expr;
417         default:
418           break;
419       }
420     }
421     return new Tree.Unary(position, expr, op);
422   }
423 
qualIdent()424   private Tree.@Nullable Expression qualIdent() {
425     int pos = position;
426     ImmutableList.Builder<Ident> bits = ImmutableList.builder();
427     bits.add(ident());
428     eat();
429     if (token == Token.LBRACK) {
430       return finishClassLiteral(pos, asClassTy(pos, bits.build()));
431     }
432     while (token == Token.DOT) {
433       eat();
434       switch (token) {
435         case IDENT:
436           bits.add(ident());
437           break;
438         case CLASS:
439           // TODO(cushon): only allow in annotations?
440           eat();
441           return new Tree.ClassLiteral(pos, asClassTy(pos, bits.build()));
442         default:
443           return null;
444       }
445       eat();
446     }
447     return new Tree.ConstVarName(pos, bits.build());
448   }
449 
ident()450   private Ident ident() {
451     return new Ident(lexer.position(), lexer.stringValue());
452   }
453 
finishClassLiteral(int pos, Tree.Type type)454   private Expression finishClassLiteral(int pos, Tree.Type type) {
455     while (token == Token.LBRACK) {
456       eat();
457       if (token != Token.RBRACK) {
458         return null;
459       }
460       eat();
461       type = new Tree.ArrTy(position, ImmutableList.of(), type);
462     }
463     if (token != Token.DOT) {
464       return null;
465     }
466     eat();
467     if (token != Token.CLASS) {
468       return null;
469     }
470     eat();
471     return new ClassLiteral(pos, type);
472   }
473 
expression()474   public Tree.Expression expression() {
475     Tree.Expression result = expression(null);
476     switch (token) {
477       case EOF:
478       case SEMI:
479         // TODO(cushon): only allow in annotations?
480       case COMMA:
481       case RPAREN:
482         return result;
483       default:
484         return null;
485     }
486   }
487 
expression(TurbineOperatorKind.Precedence prec)488   private Tree.Expression expression(TurbineOperatorKind.Precedence prec) {
489     Tree.Expression term1 = primary(false);
490     if (term1 == null) {
491       return null;
492     }
493     return expression(term1, prec);
494   }
495 
expression(Tree.Expression term1, TurbineOperatorKind.Precedence prec)496   private Tree.Expression expression(Tree.Expression term1, TurbineOperatorKind.Precedence prec) {
497     while (true) {
498       if (token == Token.EOF) {
499         return term1;
500       }
501       TurbineOperatorKind op = operator(token);
502       if (op == null) {
503         return term1;
504       }
505       if (prec != null && op.prec().rank() <= prec.rank()) {
506         return term1;
507       }
508       eat();
509       if (op == TurbineOperatorKind.TERNARY) {
510         term1 = ternary(term1);
511       } else if (op == TurbineOperatorKind.ASSIGN) {
512         term1 = assign(term1, op);
513       } else {
514         term1 = new Tree.Binary(position, term1, expression(op.prec()), op);
515       }
516       if (term1 == null) {
517         return null;
518       }
519     }
520   }
521 
assign(Tree.Expression term1, TurbineOperatorKind op)522   private Tree.Expression assign(Tree.Expression term1, TurbineOperatorKind op) {
523     if (!(term1 instanceof Tree.ConstVarName)) {
524       return null;
525     }
526     ImmutableList<Ident> names = ((Tree.ConstVarName) term1).name();
527     if (names.size() > 1) {
528       return null;
529     }
530     Ident name = getOnlyElement(names);
531     Tree.Expression rhs = expression(op.prec());
532     if (rhs == null) {
533       return null;
534     }
535     return new Tree.Assign(term1.position(), name, rhs);
536   }
537 
ternary(Tree.Expression term1)538   private Tree.Expression ternary(Tree.Expression term1) {
539     Tree.Expression thenExpr = expression(TurbineOperatorKind.Precedence.TERNARY);
540     if (thenExpr == null) {
541       return null;
542     }
543     if (token != Token.COLON) {
544       return null;
545     }
546     eat();
547     Tree.Expression elseExpr = expression();
548     if (elseExpr == null) {
549       return null;
550     }
551     return new Tree.Conditional(position, term1, thenExpr, elseExpr);
552   }
553 
castTail(TurbineConstantTypeKind ty)554   private Tree.Expression castTail(TurbineConstantTypeKind ty) {
555     if (token != Token.RPAREN) {
556       return null;
557     }
558     eat();
559     Tree.Expression rhs = primary(false);
560     if (rhs == null) {
561       return null;
562     }
563     return new Tree.TypeCast(position, new Tree.PrimTy(position, ImmutableList.of(), ty), rhs);
564   }
565 
annotation()566   private Tree.@Nullable AnnoExpr annotation() {
567     if (token != Token.AT) {
568       throw new AssertionError();
569     }
570     eat();
571     Tree.ConstVarName constVarName = (Tree.ConstVarName) qualIdent();
572     if (constVarName == null) {
573       return null;
574     }
575     ImmutableList<Ident> name = constVarName.name();
576     ImmutableList.Builder<Tree.Expression> args = ImmutableList.builder();
577     if (token == Token.LPAREN) {
578       eat();
579       while (token != Token.RPAREN) {
580         int pos = position;
581         Tree.Expression expression = expression();
582         if (expression == null) {
583           throw TurbineError.format(lexer.source(), pos, ErrorKind.INVALID_ANNOTATION_ARGUMENT);
584         }
585         args.add(expression);
586         if (token != Token.COMMA) {
587           break;
588         }
589         eat();
590       }
591       if (token == Token.RPAREN) {
592         eat();
593       }
594     }
595     return new Tree.AnnoExpr(position, new Tree.Anno(position, name, args.build()));
596   }
597 
598   @CheckReturnValue
error(ErrorKind kind, Object... args)599   private TurbineError error(ErrorKind kind, Object... args) {
600     return TurbineError.format(lexer.source(), lexer.position(), kind, args);
601   }
602 }
603