diff options
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.java | 144 |
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); + } + } +} |