package edu.ucsd.arcum.interpreter.fragments;
import static edu.ucsd.arcum.ArcumPlugin.DEBUG;
import static edu.ucsd.arcum.exceptions.ArcumError.fatalError;
import static edu.ucsd.arcum.interpreter.fragments.ModifierElement.*;
import static edu.ucsd.arcum.interpreter.fragments.SubtreeList.copyAllAndAdd;
import static edu.ucsd.arcum.interpreter.fragments.SubtreeList.Kind.ORDER_MATTERS;
import static edu.ucsd.arcum.interpreter.fragments.SubtreeList.Kind.UNORDERED;
import static edu.ucsd.arcum.interpreter.fragments.VariableNode.DONT_CARE;
import static edu.ucsd.arcum.interpreter.parser.BacktrackingScanner.TokenNameARCUMBEGINQUOTE;
import static edu.ucsd.arcum.interpreter.parser.BacktrackingScanner.TokenNameARCUMVARIABLE;
import static java.util.Collections.addAll;
import static java.util.Collections.singletonList;
import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.*;
import java.util.*;
import org.eclipse.jdt.core.dom.*;
import com.google.common.collect.Lists;
import edu.ucsd.arcum.exceptions.ArcumError;
import edu.ucsd.arcum.exceptions.JavaFragmentCompilationProblem;
import edu.ucsd.arcum.exceptions.SourceLocation;
import edu.ucsd.arcum.exceptions.UserCompilationProblem;
import edu.ucsd.arcum.interpreter.ast.FormalParameter;
import edu.ucsd.arcum.interpreter.ast.expressions.PatternExpression;
import edu.ucsd.arcum.interpreter.parser.BacktrackingScanner;
import edu.ucsd.arcum.interpreter.parser.FragmentParser;
import edu.ucsd.arcum.interpreter.parser.ArcumStructureParser.EmbeddedExpression;
import edu.ucsd.arcum.interpreter.parser.FragmentParser.TypeBindingWithModifiers;
import edu.ucsd.arcum.interpreter.query.*;
import edu.ucsd.arcum.interpreter.satisfier.TypeLookupTable;
import edu.ucsd.arcum.util.Helper;
import edu.ucsd.arcum.util.MethodGroup;
import edu.ucsd.arcum.util.StringUtil;
@SuppressWarnings("restriction")
public class ProgramFragmentFactory
{
private static final String VARIABLE_PREFIX = "ARCUM_VARIABLE_";
private static final String TRAIT_LIST_PREFIX = "ARCUM_TRAIT_LIST_PREFIX_";
private static final String VARIABLE_ANY = String.format("%sANY", VARIABLE_PREFIX);
public static final String EMBEDDED_VALUE_PREFIX = "EMBEDDED_VALUE_PREFIX_";
private final PatternExpression pattern;
private final IEntityLookup lookup;
private final List<ProgramFragment> fragments;
private final TypeLookupTable types;
private final boolean isMatchingMode;
public ProgramFragmentFactory(PatternExpression pattern, EntityType type,
IEntityLookup lookup, TypeLookupTable types, boolean isMatchingMode)
{
this.pattern = pattern;
this.lookup = lookup;
this.fragments = Lists.newArrayList();
this.types = types;
this.isMatchingMode = isMatchingMode;
String text = pattern.getPattern();
BacktrackingScanner scanner = new BacktrackingScanner(text);
SourceLocation position = pattern.getPosition();
try {
switch (type) {
case EXPR:
fragments.addAll(initialzeExpr(scanner));
break;
case FIELD:
fragments.addAll(initializeFieldDeclaration(scanner));
break;
case SIGNATURE:
fragments.addAll(initializeSignature(scanner));
break;
case ACCESS_SPECIFIER:
fragments.addAll(initializeAccessSpecifier(scanner));
break;
case TYPE:
fragments.addAll(initializeType(scanner, type));
break;
case DECLARATION_ELEMENT:
fragments.addAll(initializeDeclarationElement(scanner));
break;
case METHOD:
fragments.addAll(initializeMethod(scanner));
break;
case STATEMENT:
fragments.addAll(initializeStatement(scanner));
break;
case ANNOTATION:
fragments.addAll(initializeAnntotation(scanner));
break;
case ANY:
ArcumError.fatalUserError(position, "Not enough information to parse");
break;
case ERROR:
ArcumError.fatalUserError(position,
"Pattern must be bound in some context to a variable with a type");
break;
default:
ArcumError.fatalUserError(position,
"Matching %s's is not supported yet%n", type);
break;
}
}
catch (JavaFragmentCompilationProblem jfcp) {
String message = StringUtil.enumerate(jfcp.getMessages());
ArcumError.fatalUserError(position, "%s", message);
}
if (DEBUG) {
System.out.printf("Turned %s%nInto: %s%n", text, StringUtil.separate(
fragments, "%n"));
}
}
public List<ProgramFragment> getAbstractProgramFragments() {
if (DEBUG) {
System.out.printf("%nReturning fragments:%n");
for (ProgramFragment fragment : fragments) {
System.out.printf("%s%n", fragment);
}
System.out.printf("%n");
}
return fragments;
}
// Assuming that an arcum variable is the scanner's current token this
// will read it in from the scanner and look it up in the entity lookup.
// If it has already been found, it will return the resolved value for it.
// Otherwise, it will return a proper branch (either making it a variable
// node or a previously made node if a pattern exists for it)
private ProgramFragment matchAndLookupVariable(BacktrackingScanner scanner,
EntityType type)
{
String id = scanner.getCurrentTokenString();
scanner.match();
return resolveNodeOrCreateVariable(type, id);
}
private ProgramFragment resolveNodeOrCreateVariable(EntityType type, String id) {
Object entity = lookup.lookupEntity(id);
if (entity != null) {
return ResolvedEntity.newInstance(entity);
}
else {
return new VariableNode(type, id);
}
}
private List<ProgramFragment> initializeStatement(BacktrackingScanner scanner)
throws JavaFragmentCompilationProblem
{
String stmtString = fillInArcumVariables(scanner, new HashSet<String>());
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
Statement stmt = fragmentParser.getStatement(stmtString);
ProgramFragment result = buildSubtreeNodeFromAST(stmt, true, EntityType.STATEMENT);
return Collections.singletonList(result);
}
private List<ProgramFragment> initializeAnntotation(BacktrackingScanner scanner)
throws JavaFragmentCompilationProblem
{
String anntString = fillInArcumVariables(scanner, new HashSet<String>());
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
Annotation annt = fragmentParser.getAnnotation(anntString, pattern.getPosition());
ProgramFragment result = buildSubtreeNodeFromAST(annt, true,
EntityType.ANNOTATION);
return Collections.singletonList(result);
}
// e.g.:
// Field access expressions:
// getExpr == [`targetExpr.`field]
// setExpr == [`targetExpr.`field = `valExpr]
//
// Static field access expressions:
// fieldSet == [`targetType.`mapField = `_] (a static field access)
// getExpr == [`targetType.`mapField.get(`targetExpr)]
// setExpr == [`targetType.`mapField.put(`targetExpr, `valExpr)]
//
// Special call patterns:
// ([`e.put(`_, `_)] || [`e.get(`_)])
//
// Arbitrary expressions:
// mapInit == [new WeakIdentityHashMap<`targetType, `attrType>()]
private List<ProgramFragment> initialzeExpr(BacktrackingScanner scanner)
throws JavaFragmentCompilationProblem
{
// FIXME: finish gathering type information, and reverse the order in which
// pattern sub-clauses get created, so that more type information is known
Set<String> locals = new TreeSet<String>();
String exprString = fillInArcumVariables(scanner, locals);
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
Expression expr = fragmentParser.getExpression(exprString, locals);
ProgramFragment result = buildSubtreeNodeFromAST(expr, true, EntityType.EXPR);
return Collections.singletonList(result);
}
// MACNEIL: Is this just unused, or is there something useful in here worth keeping?
// private List<ProgramFragment> parseArgumentList(BacktrackingScanner scanner)
// throws JavaFragmentCompilationProblem
// {
// List<ProgramFragment> args = new ArrayList<ProgramFragment>();
// scanner.match(TokenNameLPAREN);
// while (scanner.lookaheadIsNot(TokenNameRPAREN, TokenNameEOF)) {
// List<ProgramFragment> exprs = parseExpression(scanner, true);
// if (exprs.size() != 1) {
// ArcumError.fatalError("Multi-patterns currently not"
// + " supported-- parseArgumentList");
// }
// args.add(exprs.get(0));
// if (scanner.lookahead() == TokenNameCOMMA)
// scanner.match(TokenNameCOMMA);
// else
// break;
// }
// scanner.match(TokenNameRPAREN);
// return args;
// }
// e.g.:
// [null]
// [5 + i]
// [new `MyType()]
// [`expr]
// Where any instances of variables that are Types, Interfaces, or Classes
// are replaced with their fully-qualified names and then parsed
private List<ProgramFragment> parseExpression(BacktrackingScanner scanner,
boolean typesExpected) throws JavaFragmentCompilationProblem
{
HashSet<String> locals = new HashSet<String>();
String exprString = fillInArcumVariables(scanner, locals);
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
Expression expr = fragmentParser.getExpression(exprString, locals);
ProgramFragment result = buildSubtreeNodeFromAST(expr, typesExpected,
EntityType.EXPR);
return singletonList(result);
}
private ProgramFragment buildSubtreeNodeFromAST(ASTNode node, boolean typesExpected,
EntityType entityType)
{
ProgramFragment result;
// if (node instanceof Assignment) {
// Assignment assignment = (Assignment)node;
// Expression lhs = assignment.getLeftHandSide();
// }
if (node instanceof FieldAccess) {
FieldAccess fieldAccess = (FieldAccess)node;
Expression target = fieldAccess.getExpression();
SimpleName fieldName = fieldAccess.getName();
ProgramFragment targetFragment = buildSubtreeNodeFromAST(target,
typesExpected, entityType);
ProgramFragment fieldFragment = lookupFieldName(fieldName);
result = makeFieldAccessNode(targetFragment, fieldFragment);
}
else if (node instanceof QualifiedName) {
QualifiedName name = (QualifiedName)node;
Name qualifier = name.getQualifier();
IBinding qualifierBinding = EntityDataBase.resolveBindingNullOK(qualifier);
if (qualifierBinding instanceof ITypeBinding) {
// then it's a class name with a field
ITypeBinding typeBinding = (ITypeBinding)qualifierBinding;
ProgramFragment targetExpr = new ResolvedType(typeBinding);
ProgramFragment field = lookupFieldName(name.getName());
result = makeFieldAccessNode(targetExpr, field);
}
else {
IBinding binding = EntityDataBase.resolveBindingNullOK(name);
if (binding != null && binding instanceof ITypeBinding) {
// then it's a fully qualified class name without a field
ITypeBinding tb = (ITypeBinding)binding;
result = new ResolvedType(tb);
}
else {
// then we assume it's s field reference of another form
ProgramFragment targetExpr = buildSubtreeNodeFromAST(qualifier,
typesExpected, entityType);
ProgramFragment field = lookupFieldName(name.getName());
result = makeFieldAccessNode(targetExpr, field);
}
}
}
else if (node instanceof SimpleName) {
SimpleName name = (SimpleName)node;
String lexeme = name.getIdentifier();
if (lexeme.equals(VARIABLE_ANY)) {
result = new VariableNode(entityType, DONT_CARE);
}
else if (lexeme.startsWith(VARIABLE_PREFIX)) {
String arcumVarName = lexeme.substring(VARIABLE_PREFIX.length());
// MACNEIL: maybe lookup its type, had a bug when just a variable
// was returned, but calling resolveNodeOrCreateVariable fixes this
result = resolveNodeOrCreateVariable(entityType, arcumVarName);
}
else {
IBinding binding = EntityDataBase.resolveBindingNullOK(name);
// URGENT (!!!): We need to fill in information when we can,
// until then, some checks will not be made
if (binding == null && false && typesExpected) {
throw new UserCompilationProblem(String.format(
"Cannot resolve \"%s\": Possible mispelling"
+ " or an import is missing", name.getFullyQualifiedName()));
}
if (binding instanceof ITypeBinding) {
ITypeBinding tb = (ITypeBinding)binding;
// Then it's the name of a class without a field access,
// e.g. as in 'new MyClass()'
result = new ResolvedType(tb);
}
else {
// Then it's some other name, e.g. a method name (and may
// not technically be an expression)
AST ast = node.getAST();
SimpleName unparentedSimpleName = ast.newSimpleName(lexeme);
result = new ResolvedEntity(unparentedSimpleName);
}
}
}
else if (isNodeVariable(node)) {
String arcumVarName = extractNodeVariable(node);
result = resolveNodeOrCreateVariable(entityType, arcumVarName);
}
else {
PartialNode partial = new PartialNode(node.getClass());
StructuralPropertyDescriptor[] spds = ASTTraverseTable.getProperties(node);
edges: for (StructuralPropertyDescriptor spd : spds) {
ProgramFragment branch;
Object property = node.getStructuralProperty(spd);
if (property == null) {
continue edges;
}
if (spd.isChildProperty()) {
ASTNode child = (ASTNode)property;
branch = buildSubtreeNodeFromAST(child, typesExpected, entityType);
}
else if (spd.isChildListProperty()) {
List children = (List)property;
if (children.size() == 0
&& canBeCanonicalizedAway((ChildListPropertyDescriptor)spd))
{
continue edges;
}
if (isListVariable(children)) {
String arcumVarName = extractListVariable(children);
branch = (ProgramFragment)lookup.lookupEntity(arcumVarName);
}
else {
List<ProgramFragment> nodes = new ArrayList<ProgramFragment>();
for (Object child : children) {
ProgramFragment element;
element = buildSubtreeNodeFromAST((ASTNode)child,
typesExpected, entityType);
nodes.add(element);
}
branch = new SubtreeList(nodes, ORDER_MATTERS);
}
}
else /* if (spd.isSimpleProperty()) */{
Object value = property;
branch = new SimplePropertyLeaf(value);
}
partial.addBranch(spd, branch);
}
result = reclassifyPartialNode(partial);
}
return result;
}
private boolean isNodeVariable(ASTNode node) {
String arcumVariable = extractNodeVariable(node);
return arcumVariable != null;
}
private String extractNodeVariable(ASTNode node) {
String arcumVariable = null;
String name = null;
if (node instanceof SimpleName) {
SimpleName simpleName = (SimpleName)node;
name = simpleName.getIdentifier();
}
else if (node instanceof MethodInvocation) {
MethodInvocation methodInvocation = (MethodInvocation)node;
name = methodInvocation.getName().getIdentifier();
}
if (name != null) {
if (name.startsWith(TRAIT_LIST_PREFIX)) {
String indexStr = name.substring(TRAIT_LIST_PREFIX.length());
arcumVariable = String.format("%s%s", EMBEDDED_VALUE_PREFIX, indexStr);
}
}
return arcumVariable;
}
private boolean isListVariable(List children) {
String arcumVariable = extractListVariable(children);
return arcumVariable != null;
}
private String extractListVariable(List children) {
if (children.size() == 1) {
String arcumVariable = null;
Object object = children.get(0);
Expression expr = null;
if (object instanceof ExpressionStatement) {
ExpressionStatement exprStmt = (ExpressionStatement)object;
expr = exprStmt.getExpression();
}
else if (object instanceof Expression) {
expr = (Expression)object;
}
if (expr != null) {
arcumVariable = extractNodeVariable(expr);
}
return arcumVariable;
}
else {
return null;
}
}
// Is the given edge something where, if the user input fragment is empty, then
// we should assume we shouldn't even inspect the edge? For example, we want to
// match a given class, even if it has annotations on it. It would only be when
// the annotations were present that they would have to match against the
// remaining list.
//
// This whole procedure can be thought of as a short-hand. For example,
// [public void foo()]
// is really short-hand for
// [`{Annotation... _} public void foo()]
// where the backtick'ed expression matches zero or more annotations and puts
// then in a dummy variable. The "modifiers"-like access to annotations in the
// JDT DOM can make this a little more complex to actually code, but the
// access-specifier hack is similar and perhaps it could be abstracted away.
// That is, the modifiers list really should be three different lists:
// (1) the list of annotations; (2) the singleton-list of the access specifier;
// and (3) the list of modifiers.
//
// MACNEIL: Put the above comment somewhere in the Arcum Users Manual.
private boolean canBeCanonicalizedAway(ChildListPropertyDescriptor edge) {
// MACNEIL: Look at above comment and make this method actually do something
// in this annotation case, but that will have to wait until list support.
if (edge == MethodInvocation.TYPE_ARGUMENTS_PROPERTY) {
return true;
}
else {
return false;
}
}
private ProgramFragment lookupFieldName(SimpleName name) {
String lexeme = name.toString();
ProgramFragment result;
if (lexeme.equals(VARIABLE_ANY)) {
result = new VariableNode(EntityType.EXPR, DONT_CARE);
}
else if (lexeme.startsWith(VARIABLE_PREFIX)) {
String arcumVarName = lexeme.substring(VARIABLE_PREFIX.length());
// MACNEIL: maybe lookup its type, had a bug when just a variable
// was returned, but calling resolveNodeOrCreateVariable fixes this
result = resolveNodeOrCreateVariable(EntityType.EXPR, arcumVarName);
}
else {
result = new ResolvedEntity(name/*$$$$$$$$$lexeme*/);
}
return result;
}
// A more specific ProgramFragment type might exist, meaning the general
// PartialNode data should become specialized
private ProgramFragment reclassifyPartialNode(PartialNode partial) {
ProgramFragment result = partial;
if (partial.getRootType().equals(Assignment.class)) {
ProgramFragment lhs = partial.lookupEdge(Assignment.LEFT_HAND_SIDE_PROPERTY);
ProgramFragment rhs = partial.lookupEdge(Assignment.RIGHT_HAND_SIDE_PROPERTY);
// // TODO: we should grab which kind of assignment it is too. For now,
// // we assume simple (i.e. not compound) assignment
if (lhs instanceof FieldAccessPattern) {
FieldAccessPattern fieldAccess = (FieldAccessPattern)lhs;
result = new FieldAssignmentPattern(fieldAccess, rhs);
}
}
return result;
}
// getExpr == [`targetType.`mapField.get(`targetExpr)]
// URGENT: field accesses embedded in the argument list may not currently be
// supported (this has not been tested)
private List<ProgramFragment> parseStaticFieldAccessMethodCall(
ProgramFragment targetExpr, ProgramFragment field, BacktrackingScanner scanner)
throws JavaFragmentCompilationProblem
{
Object fieldEntity = ((ResolvedEntity)field).getValue();
FieldDeclaration fieldASTNode = (FieldDeclaration)Entity
.getASTNodeValue(fieldEntity);
List<?> fragments = fieldASTNode.fragments();
if (fragments.size() != 1) {
ArcumError.fatalError("Cannot support multiple variable declarations yet.%n");
}
VariableDeclarationFragment varFrag = (VariableDeclarationFragment)fragments
.get(0);
SimpleName fieldVarName = varFrag.getName();
ResolvedEntity fieldVarNameEntity = new ResolvedEntity(fieldVarName);
// target expression
FieldAccessPattern expression = makeFieldAccessNode(targetExpr, field);
// method call expression
// URGENT: need to do this trick in some other method call contexts too
List<ProgramFragment> results = parseExpression(scanner, false);
check: for (ProgramFragment programFragment : results) {
if (programFragment instanceof PartialNode) {
PartialNode methodCall = (PartialNode)programFragment;
Class rootType = methodCall.getRootType();
if (MethodInvocation.class.isAssignableFrom(rootType)) {
methodCall
.addBranch(MethodInvocation.EXPRESSION_PROPERTY, expression);
continue check;
}
}
ArcumError.fatalError("Expected a method call: %s", programFragment);
}
return results;
}
private FieldAccessPattern makeFieldAccessNode(ProgramFragment targetExpr,
ProgramFragment field)
{
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
FieldAccessPattern fieldAccess = new FieldAccessPattern(targetExpr, field,
fragmentParser);
return fieldAccess;
}
// entityType is assumed to be TYPE, CLASS, or INTERFACE
private List<ProgramFragment> initializeType(BacktrackingScanner scanner,
EntityType entityType) throws JavaFragmentCompilationProblem
{
String typeString = fillInArcumVariables(scanner, new HashSet<String>());
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
// MACNEIL : This is a bit of a hack We need some other way to determine
// if it's a type declaration versus a type reference. Perhaps by seeing
// if the keywords appear *before* any punctuation like ( and {.
ProgramFragment result;
BacktrackingScanner checker = new BacktrackingScanner(typeString);
if (checker.containsToken(TokenNameinterface, TokenNameclass, TokenNameenum)) {
// Then it's a type declaration
// MACNEIL: What was this doing?
// if (typeString.contains(SET_VARIABLE_PREFIX)) {
// typeString = insertSetPlaceholder(typeString, entityType);
// }
AbstractTypeDeclaration decl = fragmentParser.getTypeDeclaration(typeString);
result = buildSubtreeNodeFromAST(decl, false, entityType);
}
else {
// Then it's a type reference
TypeBindingWithModifiers tbwm = fragmentParser.getTypeBindingWithModifiers(typeString);
result = new ResolvedType(tbwm.typeBinding);
}
return Collections.singletonList(result);
}
private List<ProgramFragment> initializeDeclarationElement(BacktrackingScanner scanner)
throws JavaFragmentCompilationProblem
{
String typeString = fillInArcumVariables(scanner, new HashSet<String>());
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
TypeBindingWithModifiers tbwm = fragmentParser.getTypeBindingWithModifiers(typeString);
ProgramFragment result = new DeclarationElement(tbwm.typeBinding, tbwm.modifiers);
return Collections.singletonList(result);
}
// MACNEIL: What was this doing?
// private String insertSetPlaceholder(String pseudoSrc, EntityType entityType) {
// if (EntityType.TYPE.isAssignableFrom(entityType)) {
// while (pseudoSrc.contains(SET_VARIABLE_PREFIX)) {
// int start = pseudoSrc.indexOf(SET_VARIABLE_PREFIX);
// int i = start;
// StringBuilder idBuilder = new StringBuilder();
// for (;;) {
// char t = pseudoSrc.charAt(i);
// if (!Character.isJavaIdentifierPart(t))
// break;
// idBuilder.append(t);
// }
// String id = idBuilder.toString();
// }
// return pseudoSrc;
// }
// else {
// ArcumError.fatalError("Can't handle this situation");
// return null;
// }
// }
private List<ProgramFragment> initializeMethod(BacktrackingScanner scanner)
throws JavaFragmentCompilationProblem
{
String text = pattern.getPattern();
if (!text.contains("{") && !text.contains(";")) {
return initializeSignature(scanner);
}
else {
String methodStr = fillInArcumVariables(scanner, new HashSet<String>());
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
MethodDeclaration methodDecl = fragmentParser.getMethod(methodStr);
ProgramFragment result = buildSubtreeNodeFromAST(methodDecl, true,
EntityType.METHOD);
return Collections.singletonList(result);
}
}
private List<ProgramFragment> initializeSignature(BacktrackingScanner scanner)
throws JavaFragmentCompilationProblem
{
String signatureStr = String.format("%s {}", fillInArcumVariables(scanner,
new HashSet<String>()));
FragmentParser fragmentParser = lookup.newParser(isMatchingMode);
MethodDeclaration methodDecl = fragmentParser.getMethod(signatureStr);
SignatureEntity signature = new SignatureEntity(methodDecl);
ProgramFragment result = new ResolvedEntity(signature);
return Collections.singletonList(result);
}
private String fillInArcumVariables(BacktrackingScanner scanner,
Set<String> localDecls)
{
StringBuilder buff = new StringBuilder();
int currentQuoted = 0;
while (scanner.lookahead() != TokenNameEOF) {
currentQuoted = _insertAndMatch(scanner, buff, currentQuoted, localDecls);
buff.append(" ");
}
return buff.toString();
}
@Helper(@MethodGroup(type = ProgramFragmentFactory.class, names = "fillInArcumVariables"))
private int _insertAndMatch(BacktrackingScanner scanner, StringBuilder buff,
int currentQuoted, Set<String> localDecls)
{
String lexeme = scanner.getCurrentTokenString();
if (scanner.lookaheadEquals(TokenNameARCUMBEGINQUOTE)) {
buff.append(TRAIT_LIST_PREFIX);
buff.append(currentQuoted);
EmbeddedExpression embed = pattern.getEmbeddedExpression(currentQuoted);
int numTokens = embed.getNumTokens();
for (int i = 0; i < numTokens; ++i) {
// eat the expression, we're just putting a place-holder for it here
scanner.match();
}
FormalParameter boundVar = embed.getBoundVar();
EntityType type = boundVar.getType();
if (type == EntityType.STATEMENT) {
// make it a valid statement
buff.append("(); ");
}
++currentQuoted;
}
else if (scanner.lookaheadEquals(TokenNameARCUMVARIABLE)) {
@Union("Entity") Object entity = lookup.lookupEntity(lexeme);
Object realEntity = entity;
if (entity instanceof SubtreeList
|| (entity instanceof ASTNode && !(entity instanceof FieldDeclaration) && !(entity instanceof TypeDeclaration)))
{
// Signal to fill it in later, since it already contains ASTNodes
// or is an ASTNode we do not need to unparse and reparse them
entity = null;
}
if (entity == null) {
String nameForTemp;
if (lexeme.equals(ArcumDeclarationTable.SPECIAL_ANY_VARIABLE)) {
nameForTemp = VARIABLE_ANY;
}
else {
nameForTemp = String.format("%s%s", VARIABLE_PREFIX, lexeme);
}
buff.append(nameForTemp);
if (realEntity instanceof Expression) {
Expression expression = (Expression)realEntity;
ITypeBinding type = expression.resolveTypeBinding();
String qualifiedName;
if (type.isNullType()) {
qualifiedName = "Object";
}
else {
qualifiedName = type.getQualifiedName();
}
localDecls.add(String.format("%s %s", qualifiedName, nameForTemp));
}
}
else if (entity instanceof ITypeBinding) {
ITypeBinding typeBinding = (ITypeBinding)entity;
String qualifiedName = typeBinding.getQualifiedName();
buff.append(qualifiedName);
}
else if (entity instanceof TypeDeclaration) {
TypeDeclaration typeDeclaration = (TypeDeclaration)entity;
String qualifiedName = typeDeclaration.resolveBinding()
.getQualifiedName();
buff.append(qualifiedName);
}
else if (entity instanceof FieldDeclaration) {
FieldDeclaration fieldDecl = (FieldDeclaration)entity;
Type type = fieldDecl.getType();
VariableDeclarationFragment var = ((VariableDeclarationFragment)fieldDecl
.fragments().get(0));
String id = var.getName().getIdentifier();
buff.append(id);
}
else if (entity instanceof String) {
String string = (String)entity;
buff.append(string);
}
else {
fatalError("Expected a resolved type here or an expression.");
}
}
else {
buff.append(lexeme);
}
scanner.match();
buff.append(" ");
return currentQuoted;
}
// e.g.: [?transient `spec `attrType `attrName]
private List<ProgramFragment> initializeFieldDeclaration(BacktrackingScanner scanner)
{
List<ProgramFragment> result = new ArrayList<ProgramFragment>();
List<? extends ProgramFragment> modifierLists = initializeModifiersList(scanner);
ProgramFragment type = parseType(scanner);
ProgramFragment name = parseVariableDeclarationFragment(scanner);
for (ProgramFragment modifierList : modifierLists) {
PartialNode fieldDecl = new PartialNode(FieldDeclaration.class);
SubtreeList fragmentsList;
fragmentsList = new SubtreeList(singletonList(name), ORDER_MATTERS);
fieldDecl.addBranch(FieldDeclaration.FRAGMENTS_PROPERTY, fragmentsList);
fieldDecl.addBranch(FieldDeclaration.TYPE_PROPERTY, type);
fieldDecl.addBranch(FieldDeclaration.MODIFIERS2_PROPERTY, modifierList);
result.add(fieldDecl);
}
return result;
}
private List<ProgramFragment> initializeAccessSpecifier(BacktrackingScanner scanner) {
ArcumError.fatalError("Not supporting access specifiers just yet");
return null;
}
private ProgramFragment parseType(BacktrackingScanner scanner) {
if (currentVariableTypeMatches(scanner, EntityType.TYPE)) {
return matchAndLookupVariable(scanner, EntityType.TYPE);
}
else {
ArcumError.fatalError("Not supporting other Type parsing just yet");
return null;
}
}
private ProgramFragment parseVariableDeclarationFragment(BacktrackingScanner scanner)
{
PartialNode varDeclFrag = new PartialNode(VariableDeclarationFragment.class);
PartialNode nameBranch = new PartialNode(SimpleName.class);
ProgramFragment identifier;
if (scanner.lookaheadEquals(TokenNameIdentifier)) {
String name = scanner.getCurrentTokenString();
scanner.match();
identifier = new SimplePropertyLeaf(name);
}
else if (currentVariableTypeMatches(scanner, EntityType.STRING)) {
identifier = matchAndLookupVariable(scanner, EntityType.STRING);
}
else {
ArcumError.fatalError("Not supporting other Variable parsing just yet");
return null;
}
nameBranch.addBranch(SimpleName.IDENTIFIER_PROPERTY, identifier);
varDeclFrag.addBranch(VariableDeclarationFragment.NAME_PROPERTY, nameBranch);
if (scanner.lookahead() == TokenNameEQUAL) {
ProgramFragment initExpr;
scanner.match(TokenNameEQUAL);
initExpr = matchAndLookupVariable(scanner, EntityType.EXPR);
varDeclFrag.addBranch(VariableDeclarationFragment.INITIALIZER_PROPERTY,
initExpr);
}
return varDeclFrag;
}
private List<? extends ProgramFragment> initializeModifiersList(
BacktrackingScanner scanner)
{
List<SubtreeList> result = new ArrayList<SubtreeList>();
while (firstOfParseModifiers(scanner)) {
int token = scanner.lookahead();
String var = scanner.getCurrentTokenString();
if (token == TokenNameQUESTION) {
scanner.match();
int mod = scanner.lookahead();
if (!ARCUM_MODIFIER_TOKEN.contains(mod)) {
ArcumError.fatalError("Invalid modifier: %s%n", var);
}
ModifierElement keyword = tokenToModifier(mod);
scanner.match();
ResolvedEntity keywordEntity = new ResolvedEntity(keyword);
List<SubtreeList> list;
list = copyAllAndAdd(result, keywordEntity, UNORDERED);
result.addAll(list);
}
else if (ARCUM_MODIFIER_TOKEN.contains(token)) {
scanner.match();
ModifierElement keyword = tokenToModifier(token);
ResolvedEntity keywordEntity = new ResolvedEntity(keyword);
SubtreeList.addToAll(result, keywordEntity, UNORDERED);
}
else if (currentVariableTypeMatches(scanner, EntityType.ACCESS_SPECIFIER)) {
String id = scanner.getCurrentTokenString();
ProgramFragment node = matchAndLookupVariable(scanner,
EntityType.ACCESS_SPECIFIER);
SubtreeList.addToAll(result, node, UNORDERED);
}
}
if (result.isEmpty()) {
ResolvedEntity spec = new ResolvedEntity(ModifierElement.MOD_PACKAGE);
ArrayList<ResolvedEntity> specList = Lists.newArrayList(spec);
result.add(new SubtreeList(specList, SubtreeList.Kind.UNORDERED));
}
return result;
}
private boolean firstOfParseModifiers(BacktrackingScanner scanner) {
int token = scanner.lookahead();
return token == TokenNameQUESTION || ARCUM_MODIFIER_TOKEN.contains(token)
|| currentVariableTypeMatches(scanner, EntityType.ACCESS_SPECIFIER);
}
static ModifierElement tokenToModifier(int mod) {
switch (mod) {
case TokenNamestatic:
return MOD_STATIC;
case TokenNameabstract:
return MOD_ABSTRACT;
case TokenNamefinal:
return MOD_FINAL;
case TokenNamenative:
return MOD_NATIVE;
case TokenNamesynchronized:
return MOD_SYNCHRONIZED;
case TokenNametransient:
return MOD_TRANSIENT;
case TokenNamevolatile:
return MOD_VOLATILE;
case TokenNamestrictfp:
return MOD_STRICTFP;
case TokenNamepublic:
return MOD_PUBLIC;
case TokenNameprotected:
return MOD_PROTECTED;
case TokenNameprivate:
return MOD_PRIVATE;
case TokenNamepackage:
return MOD_PACKAGE;
default:
return null;
}
}
private static Set<Integer> ARCUM_MODIFIER_TOKEN = new TreeSet<Integer>();
private static Set<Integer> ARCUM_ACCESS_SPECIFIER_TOKEN = new TreeSet<Integer>();
static {
addAll(ARCUM_ACCESS_SPECIFIER_TOKEN, TokenNamepublic, TokenNameprotected,
TokenNameprivate, TokenNamepackage);
addAll(ARCUM_MODIFIER_TOKEN, TokenNamestatic, TokenNameabstract, TokenNamefinal,
TokenNamenative, TokenNamesynchronized, TokenNametransient,
TokenNamevolatile, TokenNamestrictfp);
ARCUM_MODIFIER_TOKEN.addAll(ARCUM_ACCESS_SPECIFIER_TOKEN);
}
private boolean currentVariableTypeMatches(BacktrackingScanner scanner,
EntityType expectedType)
{
if (scanner.lookahead() == TokenNameARCUMVARIABLE) {
String varID = scanner.getCurrentTokenString();
if (varID.equals(ArcumDeclarationTable.SPECIAL_ANY_VARIABLE)) {
// the placeholder "dummy" variable matches all types
return true;
}
else {
EntityType type = types.lookupType(varID);
if (type == null) {
type = lookup.findResolvedSingleton(varID).getType();
}
return expectedType.isAssignableFrom(type);
}
}
else {
return false;
}
}
}