package org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.traverse;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.IMapWriter;
import org.rascalmpl.value.INode;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.ISetWriter;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.IWithKeywordParameters;
import org.rascalmpl.values.uptr.IRascalValueFactory;
import org.rascalmpl.values.uptr.ITree;
import org.rascalmpl.values.uptr.RascalValueFactory;
import org.rascalmpl.values.uptr.TreeAdapter;
public class TraverseOnceRebuild extends TraverseOnce implements ITraverseSpecialization {
public TraverseOnceRebuild(IValueFactory vf) {
super(vf);
}
private static final Map<String, IValue> emptyAnnotationsMap = new HashMap<String, IValue>();
@Override
public
IValue traverseTupleOnce(IValue subject, final TraversalState tr) {
ITuple tuple = (ITuple) subject;
int arity = tuple.arity();
boolean hasMatched = false;
boolean hasChanged = false;
IValue args[] = new IValue[arity];
for (int i = 0; i < arity; i++){
tr.setMatchedAndChanged(false, false);
args[i] = tr.traverse.once(tuple.get(i), tr);
hasMatched |= tr.hasMatched();
hasChanged |= tr.hasChanged();
}
tr.setMatchedAndChanged(hasMatched, hasChanged);
return vf.tuple(args);
}
@Override
public
IValue traverseADTOnce(IValue subject, final TraversalState tr) {
IConstructor cons = (IConstructor)subject;
boolean hasKwParams = false;
int arity = cons.arity();
if (cons.mayHaveKeywordParameters() && cons.asWithKeywordParameters().hasParameters()) {
hasKwParams = true;
}
if (arity == 0 && !hasKwParams) {
return subject; // constants have no children to traverse into
}
boolean hasChanged = false;
boolean hasMatched = false;
IValue args[] = new IValue[arity];
for (int i = 0; i < arity; i++){
IValue child = cons.get(i);
tr.setMatchedAndChanged(false, false);
args[i] = tr.traverse.once(child, tr);
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
}
Map<String, IValue> kwParams = new HashMap<>();
if (hasKwParams) {
IWithKeywordParameters<? extends INode> consKw = cons.asWithKeywordParameters();
for (String kwName : consKw.getParameterNames()) {
IValue val = consKw.getParameter(kwName);
tr.setMatchedAndChanged(false, false);
IValue newVal = tr.traverse.once(val, tr);
kwParams.put(kwName, newVal);
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
}
}
tr.setMatchedAndChanged(hasMatched, hasChanged);
if (tr.hasChanged()) {
return rebuild(subject, args, kwParams);
}
else {
return subject;
}
}
@Override
public
IValue traverseConcreteTreeOnce(IValue subject, final TraversalState tr) {
ITree tree = (ITree)subject;
if (tree.isAppl()) {
return traverseApplOnce(tr, tree);
}
else if (tree.isAmb()) {
return traverseAmbOnce(tr, tree);
}
else if (tree.isChar()) {
return tree;
}
else {
assert tree.isCycle();
return tree;
}
}
private IValue traverseApplOnce(final TraversalState tr, ITree tree) {
// - Copy prod node verbatim to result
// - Only visit non-layout nodes in argument list
IList list = TreeAdapter.getArgs(tree);
int len = list.length();
IValue[] args = new IValue[2];
if (len > 0) {
args[0] = TreeAdapter.getProduction(tree);
IListWriter w = vf.listWriter();
boolean hasChanged = false;
boolean hasMatched = false;
boolean isTop = TreeAdapter.isTop(tree);
if (isTop) {
w.append(list.get(0)); // copy layout before
tr.setMatchedAndChanged(false, false);
w.append(tr.traverse.once(list.get(1), tr));
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
w.append(list.get(2)); // copy layout after
}
else {
for (int i = 0; i < len; i++){
IValue elem = list.get(i);
if (i % 2 == 0) { // Recursion to all non-layout elements
tr.setMatchedAndChanged(false, false);
w.append(tr.traverse.once(elem, tr));
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
} else { // Just copy layout elements
w.append(list.get(i));
}
}
}
tr.setMatchedAndChanged(hasMatched, hasChanged);
args[1] = w.done();
} else {
args[1] = list;
}
if(tr.hasChanged()){
return vf.constructor(RascalValueFactory.Tree_Appl, args);
} else {
return tree;
}
}
private IValue traverseAmbOnce(final TraversalState tr, ITree tree) {
tr.setMatchedAndChanged(false, false);
boolean hasChanged = false;
boolean hasMatched = false;
ISetWriter newAlts = vf.setWriter();
for (IValue alt : tree.getAlternatives()) {
tr.setMatchedAndChanged(false, false);
newAlts.insert(tr.traverse.once(alt, tr));
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
}
tr.setMatchedAndChanged(hasMatched, hasChanged);
if (hasChanged) {
return IRascalValueFactory.getInstance().amb(newAlts.done());
}
else {
return tree;
}
}
@Override
public
IValue traverseMapOnce(IValue subject, final TraversalState tr) {
IMap map = (IMap) subject;
if(!map.isEmpty()){
Iterator<Entry<IValue,IValue>> iter = map.entryIterator();
boolean hasChanged = false;
boolean hasMatched = false;
int mapSize = map.size();
IValue[] keys = new IValue[mapSize];
IValue[] vals = new IValue[mapSize];
int i = 0;
while (iter.hasNext()) {
Entry<IValue,IValue> entry = iter.next();
tr.setMatchedAndChanged(false, false);
keys[i] = tr.traverse.once(entry.getKey(), tr);
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
tr.setMatchedAndChanged(false, false);
vals[i] = tr.traverse.once(entry.getValue(), tr);
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
i++;
}
tr.setChanged(hasChanged);
tr.setMatched(hasMatched);
if(hasChanged){
IMapWriter w = vf.mapWriter();
for(int j = 0; j < mapSize; j++){
w.put(keys[j], vals[j]);
}
return w.done();
}
return subject;
} else {
return subject;
}
}
@Override
public
IValue traverseSetOnce(IValue subject, final TraversalState tr) {
ISet set = (ISet) subject;
if(!set.isEmpty()){
boolean hasChanged = false;
boolean hasMatched = false;
int setSize = set.size();
IValue[] vals = new IValue[setSize];
int i = 0;
for (IValue v : set) {
tr.setMatchedAndChanged(false, false);
vals[i] = tr.traverse.once(v, tr);
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
i++;
}
tr.setMatchedAndChanged(hasMatched, hasChanged);
if(hasChanged){
ISetWriter w = vf.setWriter();
for(int j = 0; j < setSize; j++){
w.insert(vals[j]);
}
return w.done();
}
return subject;
} else {
return subject;
}
}
@Override
public
IValue traverseListOnce(IValue subject, final TraversalState tr) {
IList list = (IList) subject;
int len = list.length();
if (len > 0){
boolean hasChanged = false;
boolean hasMatched = false;
IListWriter w = vf.listWriter();
for (int i = 0; i < len; i++){
IValue elem = list.get(i);
tr.setMatchedAndChanged(false, false);
elem = tr.traverse.once(elem, tr);
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
w.append(elem);
}
tr.setMatchedAndChanged(hasMatched, hasChanged);
if(hasChanged){
return w.done();
} else {
return subject;
}
} else {
return subject;
}
}
@Override
public
IValue traverseNodeOnce(IValue subject, final TraversalState tr) {
IValue result= subject;
INode node = (INode)subject;
int arity = node.arity();
boolean hasKwParams = false;
if(node.mayHaveKeywordParameters() && node.asWithKeywordParameters().hasParameters()){
hasKwParams = true;
}
if (arity == 0 && !hasKwParams){
result = subject;
}
boolean hasChanged = false;
boolean hasMatched = false;
IValue args[] = new IValue[node.arity()];
Map<String, IValue> kwParams = null;
for (int i = 0; i < arity; i++){
IValue child = node.get(i);
tr.setMatchedAndChanged(false, false);
args[i] = tr.traverse.once(child, tr);
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
}
if (hasKwParams) {
kwParams = new HashMap<>();
IWithKeywordParameters<? extends INode> nodeKw = node.asWithKeywordParameters();
for (String kwName : nodeKw.getParameterNames()) {
IValue val = nodeKw.getParameter(kwName);
tr.setMatchedAndChanged(false, false);
IValue newVal = tr.traverse.once(val, tr);
kwParams.put(kwName, newVal);
hasChanged |= tr.hasChanged();
hasMatched |= tr.hasMatched();
}
}
tr.setMatchedAndChanged(hasMatched, hasChanged);
if(hasChanged){
INode n = null;
if (kwParams != null) {
n = vf.node(node.getName(), args, kwParams);
}
else {
n = vf.node(node.getName(), args);
if (!node.mayHaveKeywordParameters() && node.asAnnotatable().hasAnnotations()) {
n = n.asAnnotatable().setAnnotations(node.asAnnotatable().getAnnotations());
}
}
result = n;
}
return result;
}
@Override
public
IValue traverseStringOnce(IValue subject, final TraversalState tr) {
boolean hasMatched = tr.hasMatched();
boolean hasChanged = tr.hasChanged();
tr.setMatchedAndChanged(false, false);
IValue res = traverseString(subject, tr);
tr.setMatchedAndChanged(tr.hasMatched() | hasMatched,
tr.hasChanged() | hasChanged);
return res;
}
/*
* traverseString implements a visit of a string subject by visiting subsequent substrings
* subject[0,len], subject[1,len] ...and trying to match the cases. If a case matches
* the subject cursor is advanced by the length of the match and the matched substring may be replaced.
* At the end, the subject string including all replacements is returned.
*
* Performance issue: we create a lot of garbage by producing all these substrings.
*/
private IValue traverseString(IValue subject, final TraversalState tr){
IString subjectIString = (IString) subject;
String subjectString = subjectIString.getValue();
int len = subjectIString.length();
int subjectCursor = 0;
boolean hasMatched = false;
boolean hasChanged = false;
StringBuffer replacementString = new StringBuffer(len);
while (subjectCursor < len){
tr.setMatchedAndChanged(false, false);
tr.setBegin(subjectCursor);
tr.setEnd(len);
String repl = ((IString) traverseTop(subject, tr)).getValue();
if(tr.hasMatched()){
if(tr.getBegin() > subjectCursor){
replacementString.append(subjectString.substring(subjectCursor, tr.getBegin()));
}
replacementString.append(repl);
subjectCursor = tr.getEnd();
} else {
replacementString.append(subjectString.substring(subjectCursor, subjectCursor + 1));
subjectCursor++;
}
hasMatched |= tr.hasMatched();
hasChanged |= tr.hasChanged();
}
tr.setMatchedAndChanged(tr.hasMatched() | hasMatched,
tr.hasChanged() | hasChanged);
if (!tr.hasChanged()) {
return subject;
}
return vf.string(replacementString.toString());
}
@SuppressWarnings("deprecation")
private INode rebuild(IValue subject, IValue[] args, Map<String,IValue> kwargs) {
Map<String, IValue> annotations = subject.isAnnotatable() ? subject.asAnnotatable().getAnnotations() : emptyAnnotationsMap;
// TODO: jurgen can be optimized for the ITree case
if(subject.getType().isAbstractData()){
IConstructor cons1 = (IConstructor) subject;
IConstructor cons2 = vf.constructor(cons1.getConstructorType(), args, kwargs);
if(annotations.size() > 0){
// TODO: @paulklint what about the keyword parameters?
cons2 = cons2.asAnnotatable().setAnnotations(annotations);
}
return cons2;
} else {
INode node1 = (INode) subject;
INode node2 = vf.node(node1.getName(), args, kwargs);
if(annotations.size() > 0){
node2 = node2.asAnnotatable().setAnnotations(annotations);
}
return node2;
}
}
}