/*
* This file is part of the OpenJML project.
* Author: David R. Cok
*/
package com.sun.tools.javac.parser;
import java.lang.reflect.Constructor;
import org.jmlspecs.annotation.NonNull;
import org.jmlspecs.annotation.Nullable;
import org.jmlspecs.openjml.JmlTokenKind;
import org.jmlspecs.openjml.Utils;
import org.jmlspecs.openjml.JmlTree.JmlExpression;
import org.jmlspecs.openjml.JmlTree.JmlMethodInvocation;
import org.jmlspecs.openjml.esc.JmlAssertionAdder;
import org.jmlspecs.openjml.ext.Arithmetic.Safe;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.comp.JmlAttr;
import com.sun.tools.javac.parser.Tokens.Token;
import com.sun.tools.javac.parser.Tokens.TokenKind;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Log.WriterKind;
/* FIXME - do more to implement extensions */
/* TODO - needs documentation */
abstract public class ExpressionExtension {
protected static final Context.Key<ExpressionExtension> exprExtensionsKey =
new Context.Key<ExpressionExtension>();
/** The compilation context, set when derived classes are instantiated */
protected /*@ non_null */ Context context;
/** The parser in use, set when derived classes are instantiated */
protected /*@ non_null */ JmlParser parser;
/** The symbol table, set when the context is set */
protected Symtab syms;
/** The Utils instance */
protected Utils utils;
//@ public constraint context == \old(context);
public static void register(Context context) {}
/** A constructor needed by derived classes; this class should not be
* instantiated directly by users.
*/
protected ExpressionExtension(Context context) {
this.context = context;
this.syms = Symtab.instance(context);
this.utils = Utils.instance(context);
}
/** Writes an error message to the log, using the given DiagnosticPosition
* (typically gotten from tree.pos()),
* a key (as in the file org.jmlspecs.openjml.messages.properties)
* and arguments for that key
* @param pos the DiagnosticPosition used to identify the relevant location in the source file
* @param key the resource key holding the error message
* @param args (non-null) arguments for the key - there must be at least as many arguments as there are place-holders in the key string
*/
public void error(DiagnosticPosition pos, String key, Object ... args) {
Log.instance(context).error(pos,key,args);
}
/** Writes a warning message to the log, using the given DiagnosticPosition
* (typically gotten from tree.pos()), a key (as in the file org.jmlspecs.openjml.messages.resources)
* and arguments for that key
* @param pos the DiagnosticPosition used to identify the relevant location in the source file
* @param key the resource key holding the error message
* @param args (non-null) arguments for the key - there must be at least as many arguments as there are place-holders in the key string
*/
public void warning(DiagnosticPosition pos, String key, Object ... args) {
Log.instance(context).warning(pos,key,args);
}
/** Writes an informational message to the log's noticeWriter (as with
* println). To be used for informational or debugging information.
* @param msg the String to write
*/
public void info(@NonNull String msg) {
Log.instance(context).getWriter(WriterKind.NOTICE).println(msg);
}
/** Sets the end position of the given tree node to be the end position of
* the previously scanned token.
* @param <T> the type of the node being set
* @param tree the node whose end position is being set
* @return returns the same node
*/
public <T extends JCTree> T toP(T tree) {
return parser.toP(tree);
}
/** Called by JmlParser when it sees the token for this extension.
* The derived class implementation is responsible to scan tokens using
* the scanner (JmlParser.getScanner()) and return a JCExpression parse
* tree. When called, the current scanner token is the JmlToken itself;
* this method is responsible to scan the end of the expression (e.g. the
* terminating parenthesis) and no more. If an error occurs because of
* badly formed input, the method is required to return null and to
* recover as best it can. [ FIXME - return JCErroneous?]
* <BR>Useful methods:
* <BR>JmlParser.getScanner() - returns the current JmlScanner
* <BR>JmlScanner.token() - the current Java token, or CUSTOM if a JML token
* <BR>JmlScanner.jmlToken() - the current JML token (null if a Java token)
* <BR>JmlScanner.nextToken() - scans the next token
* <BR>JmlScanner.pos() - the current character position, used for error reporting
* <BR>JmlParser.syntaxError(...) - to report a parsing error
* <BR>TODO: warning and error and informational messages
* <BR>TODO: maker(), at(), primarySuffix, toP(), arguments(), others
*
* @param parser the JmlParser for this compilation unit and compilation context
* @param typeArgs any type arguments already seen (may be null)
* @return the AST for the expression
*/
public JCExpression parse(JmlParser parser,
@Nullable List<JCExpression> typeArgs) {
// TODO Auto-generated method stub
this.parser = parser;
JmlTokenKind jt = parser.jmlTokenKind();
int p = parser.pos();
parser.nextToken();
if (parser.token().kind != TokenKind.LPAREN) {
return parser.syntaxError(p, null, "jml.args.required", jt.internedName());
} else if (typeArgs != null && !typeArgs.isEmpty()) {
return parser.syntaxError(p, null, "jml.no.typeargs.allowed", jt.internedName());
} else {
int pp = parser.pos();
List<JCExpression> args = parser.arguments();
JmlMethodInvocation t = toP(parser.maker().at(pp).JmlMethodInvocation(jt, args));
t.startpos = p;
checkParse(parser,t);
return parser.primarySuffix(t, typeArgs);
}
}
public JCExpression parseNoArgs(JmlParser parser,
@Nullable List<JCExpression> typeArgs) {
// TODO Auto-generated method stub
this.parser = parser;
JmlTokenKind jt = parser.jmlTokenKind();
int p = parser.pos();
parser.nextToken();
if (parser.token().kind == TokenKind.LPAREN) {
return parser.syntaxError(p, null, "jml.no.args.allowed", jt.internedName());
} else if (typeArgs != null && !typeArgs.isEmpty()) {
return parser.syntaxError(p, null, "jml.no.typeargs.allowed", jt.internedName());
} else {
return parser.jmlF.at(p).JmlSingleton(jt);
}
}
abstract public void checkParse(JmlParser parser, JmlMethodInvocation e);
public void checkOneArg(JmlParser parser, JmlMethodInvocation e) {
if (e.args.size() != 1) {
parser.jmlerror(e.pos, parser.getEndPos(e), "jml.one.arg", e.token.internedName());
}
}
public void checkNoArgs(JmlParser parser, JmlMethodInvocation e) {
if (e.args.size() != 0) {
parser.jmlerror(e.pos, parser.getEndPos(e), "jml.no.args.allowed", e.token.internedName());
}
}
// TODO: document
abstract public Type typecheck(JmlAttr attr, JCExpression expr, Env<AttrContext> env);
// TODO: document
public JCExpression assertion(JmlAssertionAdder adder, JCExpression that) {
return null;
}
public static <T> T instance(Context context, Class<T> key) {
T s = context.get(key);
if (s == null) {
try {
Constructor<T> c = key.getConstructor(Context.class);
s = c.newInstance(context);
context.put(key, s);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return s;
}
}