return result;
}
private IValue traverseADTOnce(IValue subject, CaseBlockList casesOrRules,
DIRECTION direction, PROGRESS progress, FIXEDPOINT fixedpoint, TraverseResult tr) {
IConstructor cons = (IConstructor)subject;
Map<String, IValue> kwParams = null;
if (cons.mayHaveKeywordParameters() && cons.asWithKeywordParameters().hasParameters()) {
kwParams = new HashMap<>();
}
if (cons.arity() == 0 && kwParams == null) {
return subject; // constants have no children to traverse into
}
if (casesOrRules.hasAllConcretePatternCases() && TreeAdapter.isChar(cons)) {
return subject; // we dont traverse into the structure of literals and characters
}
IValue args[] = new IValue[cons.arity()];
if (casesOrRules.hasAllConcretePatternCases() && TreeAdapter.isAppl(cons)){
// Constructor is "appl": we are dealing with a syntax tree
// - Lexical or literal are returned immediately
if (TreeAdapter.isLexical(cons)|| TreeAdapter.isLiteral(cons)){
return subject; // we dont traverse into the structure of literals, lexicals, and characters
}
// Otherwise:
// - Copy prod node verbatim to result
// - Only visit non-layout nodes in argument list
args[0] = cons.get(0);
IList list = (IList) cons.get(1);
int len = list.length();
if (len > 0) {
IListWriter w = eval.getValueFactory().listWriter(list.getType().getElementType());
boolean hasChanged = false;
boolean hasMatched = false;
for (int i = 0; i < len; i++){
IValue elem = list.get(i);
if (i % 2 == 0) { // Recursion to all non-layout elements
tr.changed = false;
tr.matched = false;
w.append(traverseOnce(elem, casesOrRules, direction, progress, fixedpoint, tr));
hasChanged |= tr.changed;
hasMatched |= tr.matched;
} else { // Just copy layout elements
w.append(list.get(i));
}
}
tr.changed = hasChanged;
tr.matched = hasMatched;
args[1] = w.done();
} else {
args[1] = list;
}
} else {
// Constructor is not "appl", or at least one of the patterns is not a concrete pattern
boolean hasChanged = false;
boolean hasMatched = false;
for (int i = 0; i < cons.arity(); i++){
IValue child = cons.get(i);
tr.matched = false;
tr.changed = false;
args[i] = traverseOnce(child, casesOrRules, direction, progress, fixedpoint, tr);
hasChanged |= tr.changed;
hasMatched |= tr.matched;
}
if (kwParams != null) {
IWithKeywordParameters<? extends INode> consKw = cons.asWithKeywordParameters();
for (String kwName : consKw.getParameterNames()) {
IValue val = consKw.getParameter(kwName);
tr.changed = false;
tr.matched = false;
IValue newVal = traverseOnce(val, casesOrRules, direction, progress, fixedpoint, tr);
kwParams.put(kwName, newVal);
hasChanged |= tr.changed;
hasMatched |= tr.matched;
}
}
tr.matched = hasMatched;
tr.changed = hasChanged;
}
if (tr.changed) {
IConstructor rcons;
try {
QualifiedName n = Names.toQualifiedName(cons.getType().getName(), cons.getName(), null);
rcons = (IConstructor) eval.call(n, kwParams != null ? kwParams : Collections.<String,IValue>emptyMap(), args);
}
catch (UndeclaredFunction | UndeclaredModule | ArgumentsMismatch e) {
// This may happen when visiting data constructors dynamically which are not
// defined in the current scope. For example, when data was serialized and the format
// has changed in the meantime. We issue a warning, because it is indicative of a bug
// and normalizing "rewrite rules" will not trigger at all, but we can gracefully continue
// because we know what the tree looked like before we started visiting.
eval.warning("In visit: " + e.getMessage(), eval.getCurrentAST().getLocation());
if (kwParams != null) {
rcons = (IConstructor) eval.getValueFactory().constructor(cons.getConstructorType(), args, kwParams);
}
else {
rcons = (IConstructor) eval.getValueFactory().constructor(cons.getConstructorType(), args);
}
}
if (!cons.mayHaveKeywordParameters() && cons.asAnnotatable().hasAnnotations()) {
rcons = rcons.asAnnotatable().setAnnotations(cons.asAnnotatable().getAnnotations());
}
return rcons;
}
else {