aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java')
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java144
1 files changed, 144 insertions, 0 deletions
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java
new file mode 100644
index 0000000000..1ac9d09309
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/trees/TreeParser.java
@@ -0,0 +1,144 @@
+package org.checkerframework.javacutil.trees;
+
+import java.util.StringTokenizer;
+
+import javax.annotation.processing.ProcessingEnvironment;
+
+import com.sun.source.tree.ExpressionTree;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Names;
+
+/**
+ * A Utility class for parsing Java expression snippets, and converting them
+ * to proper Javac AST nodes.
+ *
+ * This is useful for parsing {@code EnsuresNonNull*},
+ * and {@code KeyFor} values.
+ *
+ * Currently, it handles four tree types only:
+ * <ul>
+ * <li>Identifier tree (e.g. {@code id})</li>
+ * <li>Literal tree (e.g. 2, 3)</li>
+ * <li>Method invocation tree (e.g. {@code method(2, 3)})</li>
+ * <li>Member select tree (e.g. {@code Class.field}, {@code instance.method()})
+ * <li>Array access tree (e.g. {@code array[id]})</li>
+ * </ul>
+ *
+ * Notable limitation: Doesn't handle spaces, or non-method-argument
+ * parenthesis.
+ *
+ * It's implemented via a Recursive-Descend parser.
+ */
+public class TreeParser {
+ private static final String DELIMS = ".[](),";
+ private static final String SENTINAL = "";
+
+ private final TreeMaker maker;
+ private final Names names;
+
+ public TreeParser(ProcessingEnvironment env) {
+ Context context = ((JavacProcessingEnvironment)env).getContext();
+ maker = TreeMaker.instance(context);
+ names = Names.instance(context);
+ }
+
+ /**
+ * Parses the snippet in the string as an internal Javac AST expression
+ * node
+ *
+ * @param s the java snippet
+ * @return the AST corresponding to the snippet
+ */
+ public ExpressionTree parseTree(String s) {
+ tokenizer = new StringTokenizer(s, DELIMS, true);
+ token = tokenizer.nextToken();
+
+ try {
+ return parseExpression();
+ } catch (Exception e) {
+ throw new ParseError(e);
+ } finally {
+ tokenizer = null;
+ token = null;
+ }
+ }
+
+ StringTokenizer tokenizer = null;
+ String token = null;
+
+ private String nextToken() {
+ token = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : SENTINAL;
+ return token;
+ }
+
+ JCExpression fromToken(String token) {
+ // Optimization
+ if ("true".equals(token)) {
+ return maker.Literal(true);
+ } else if ("false".equals(token)) {
+ return maker.Literal(false);
+ }
+
+ if (Character.isLetter(token.charAt(0))) {
+ return maker.Ident(names.fromString(token));
+ }
+
+ Object value = null;
+ try {
+ value = Integer.valueOf(token);
+ } catch (Exception e2) { try {
+ value = Double.valueOf(token);
+ } catch (Exception ef) {}}
+ assert value != null;
+ return maker.Literal(value);
+ }
+
+ JCExpression parseExpression() {
+ JCExpression tree = fromToken(token);
+
+ while (tokenizer.hasMoreTokens()) {
+ String delim = nextToken();
+ if (".".equals(delim)) {
+ nextToken();
+ tree = maker.Select(tree,
+ names.fromString(token));
+ } else if ("(".equals(delim)) {
+ nextToken();
+ ListBuffer<JCExpression> args = new ListBuffer<>();
+ while (!")".equals(token)) {
+ JCExpression arg = parseExpression();
+ args.append(arg);
+ if (",".equals(token)) {
+ nextToken();
+ }
+ }
+ // For now, handle empty args only
+ assert ")".equals(token);
+ tree = maker.Apply(List.<JCExpression>nil(),
+ tree, args.toList());
+ } else if ("[".equals(token)) {
+ nextToken();
+ JCExpression index = parseExpression();
+ assert "]".equals(token);
+ tree = maker.Indexed(tree, index);
+ } else {
+ return tree;
+ }
+ }
+
+ return tree;
+ }
+
+ class ParseError extends RuntimeException {
+ private static final long serialVersionUID = 1887754619522101929L;
+
+ ParseError(Throwable cause) {
+ super(cause);
+ }
+ }
+}