package org.rascalmpl.library; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import org.rascalmpl.interpreter.control_exceptions.Throw; import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory; import org.rascalmpl.interpreter.utils.LimitedResultWriter.IOLimitReachedException; import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ICallableCompiledValue; import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.RascalExecutionContext; import org.rascalmpl.repl.LimitedLineWriter; import org.rascalmpl.value.IBool; import org.rascalmpl.value.IConstructor; import org.rascalmpl.value.IInteger; 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.ISourceLocation; import org.rascalmpl.value.IString; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.value.io.StandardTextWriter; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import org.rascalmpl.value.type.TypeStore; import org.rascalmpl.values.uptr.ITree; import org.rascalmpl.values.uptr.ProductionAdapter; import org.rascalmpl.values.uptr.RascalValueFactory; import org.rascalmpl.values.uptr.SymbolAdapter; import org.rascalmpl.values.uptr.TreeAdapter; import org.rascalmpl.values.uptr.visitors.TreeVisitor; /* * This class overrides methods from Prelude that need to be handled differenty in compiled code. * In most (all?) cases this will be library function with a @reflect{...} tag that makes them dependent on * IEvaluatorContext, the context of the Rascal interpreter. */ public class PreludeCompiled extends Prelude { public PreludeCompiled(IValueFactory values) { super(values); } public void print(IValue arg, RascalExecutionContext rex){ PrintWriter currentOutStream = rex.getStdOut(); try{ if(arg.getType().isString()){ currentOutStream.print(((IString) arg).getValue().toString()); } else if(arg.getType().isSubtypeOf(RascalValueFactory.Tree)){ currentOutStream.print(TreeAdapter.yield((IConstructor) arg)); } else if (arg.getType().isSubtypeOf(RascalValueFactory.Type)) { currentOutStream.print(SymbolAdapter.toString((IConstructor) ((IConstructor) arg).get("symbol"), false)); } else{ currentOutStream.print(arg.toString()); } } finally { currentOutStream.flush(); } } @SuppressWarnings("deprecation") public INode delAnnotations(INode node, RascalExecutionContext ctx) { if (node.isAnnotatable()) { return node.asAnnotatable().removeAnnotations(); } else { ctx.getStdErr().println("Trying to remove annotations from a node which has keyword parameters."); return node; } } @SuppressWarnings("deprecation") public INode delAnnotation(INode node, IString label, RascalExecutionContext ctx) { if (node.isAnnotatable()) { return node.asAnnotatable().removeAnnotation(label.getValue()); } else { ctx.getStdErr().println("Trying to remove annotations from a node which has keyword parameters."); return node; } } public void iprint(IValue arg, IInteger lineLimit, RascalExecutionContext rex){ StandardTextWriter w = new StandardTextWriter(true, 2); Writer output = rex.getStdOut(); if (lineLimit.signum() > 0) { output = new LimitedLineWriter(output, lineLimit.longValue()); } try { w.write(arg, output); } catch (IOLimitReachedException e) { // ignore since we wanted this } catch (IOException e) { throw RuntimeExceptionFactory.io(values.string("Could not print indented value"), null, null); } finally { if (output != rex.getStdOut()) { try { output.flush(); output.close(); } catch (IOException e) { } } rex.getStdOut().flush(); } } public void iprintln(IValue arg, IInteger lineLimit, RascalExecutionContext rex){ iprint(arg, lineLimit, rex); rex.getStdOut().println(); rex.getStdOut().flush(); } public void println(RascalExecutionContext rex) { rex.getStdOut().println(); rex.getStdOut().flush(); } public void println(IValue arg, RascalExecutionContext rex){ PrintWriter currentOutStream = rex.getStdOut(); try{ if(arg.getType().isString()){ currentOutStream.print(((IString) arg).getValue()); } else if(arg.getType().isSubtypeOf(RascalValueFactory.Tree)){ currentOutStream.print(TreeAdapter.yield((IConstructor) arg)); } else if (arg.getType().isSubtypeOf(RascalValueFactory.Type)) { currentOutStream.print(SymbolAdapter.toString((IConstructor) ((IConstructor) arg).get("symbol"), false)); } else{ currentOutStream.print(arg.toString()); } currentOutStream.println(); } finally { currentOutStream.flush(); } } public void rprintln(IValue arg, RascalExecutionContext rex){ PrintWriter currentOutStream = rex.getStdOut(); try { currentOutStream.print(arg.toString()); currentOutStream.println(); } finally { currentOutStream.flush(); } } public void rprint(IValue arg, RascalExecutionContext rex){ PrintWriter currentOutStream = rex.getStdOut(); try { currentOutStream.print(arg.toString()); } finally { currentOutStream.flush(); } } // Begin of sorting functions /** * A mini class to wrap a lessThan function */ private class Less { private final ICallableCompiledValue less; Less(ICallableCompiledValue less) { this.less = less; } public boolean less(IValue x, IValue y) { return ((IBool) less.call(new Type[] { x.getType(), y.getType() }, new IValue[] { x, y }, null)).getValue(); } } private class Sorting { private final IValue[] array; private final int size; private final Less less; private void swap(int i, int j) { IValue tmp = array[i]; array[i] = array[j]; array[j] = tmp; } public Sorting(IValue[] array, Less less) { this.array = array; this.size = array.length; this.less = less; } /** * @throws IllegalArgument if comparator is illegal (i.e., if pivot equals pivot) */ public Sorting sort() { if (size == 0) { return this; } if(less.less(array[0], array[0])) { throw RuntimeExceptionFactory.illegalArgument(null, null); // "Bad comparator: Did you use less-or-equals instead of less-than?" } sort(0, size - 1); return this; } public Sorting shuffle() { for (int i = 0; i < size; i++) { swap(i, i + (int) (Math.random() * (size-i))); } return this; } private void sort(int low, int high) { IValue pivot = array[low + (high-low)/2]; int oldLow = low; int oldHigh = high; while (low < high) { for ( ; less.less(array[low], pivot); low++); for ( ; less.less(pivot, array[high]); high--); if (low <= high) { swap(low, high); low++; high--; } } if (oldLow < high) sort(oldLow, high); if (low < oldHigh) sort(low, oldHigh); } } public IList sort(IList l, IValue cmpv){ IValue[] tmpArr = new IValue[l.length()]; for(int i = 0 ; i < l.length() ; i++){ tmpArr[i] = l.get(i); } // we randomly swap some elements to make worst case complexity unlikely new Sorting(tmpArr, new Less((ICallableCompiledValue) cmpv)).shuffle().sort(); IListWriter writer = values.listWriter(); writer.append(tmpArr); return writer.done(); } public IList sort(ISet l, IValue cmpv) { IValue[] tmpArr = new IValue[l.size()]; int i = 0; // we assume that the set is reasonably randomly ordered, such // that the worst case of quicksort is unlikely for (IValue elem : l){ tmpArr[i++] = elem; } new Sorting(tmpArr, new Less((ICallableCompiledValue) cmpv)).sort(); IListWriter writer = values.listWriter(); for(IValue v : tmpArr){ writer.append(v); } return writer.done(); } // end of sorting functions // public java &T<:Tree parse(type[&T<:Tree] begin, str input); public IValue parse(IValue start, ISourceLocation input, IBool allowAmbiguity, RascalExecutionContext rex) { return rex.getParsingTools().parse(super.values.string(rex.getFullModuleName()), start, input, allowAmbiguity.getValue(), null, rex); } // public java &T<:Tree parse(type[&T<:Tree] begin, str input, loc origin); public IValue parse(IValue start, IString input, IBool allowAmbiguity, RascalExecutionContext rex) { return rex.getParsingTools().parse(super.values.string(rex.getFullModuleName()), start, input, allowAmbiguity.getValue(), null, rex); } private TypeStore typeStore = new TypeStore(); public IConstructor makeConstructor(Type returnType, String name, RascalExecutionContext rex, IValue ...args) { // TODO: in general, the following should be the call to an overloaded function IValue value = values.constructor(typeStore.lookupConstructor(returnType, name, TypeFactory.getInstance().tupleType(args)), args, new HashMap<String, IValue>()); Type type = value.getType(); if (type.isAbstractData()) { return (IConstructor)value; } throw RuntimeExceptionFactory.implodeError("Calling of constructor " + name + " did not return a constructor", null, null); } /*** begin of implode **/ private IConstructor makeConstructor(TypeStore store, Type returnType, String name, IValue ...args) { // TODO: in general, the following should be the call to an overloaded function IValue value = values.constructor(store.lookupConstructor(returnType, name, TypeFactory.getInstance().tupleType(args)), args, new HashMap<String, IValue>()); Type type = value.getType(); if (type.isAbstractData()) { return (IConstructor)value; } throw RuntimeExceptionFactory.implodeError("Calling of constructor " + name + " did not return a constructor", null, null); } public IValue implode(IValue reifiedType, IConstructor arg0, RascalExecutionContext rex) { ITree tree = (ITree) arg0; typeStore = new TypeStore(); Type type = tr.valueToType((IConstructor) reifiedType, typeStore); try { IValue result = implode(typeStore, type, tree, false, rex); if (isUntypedNodeType(type) && !type.isTop() && (TreeAdapter.isList(tree) || TreeAdapter.isOpt(tree))) { // Ensure the result is actually a node, even though // the tree given to implode is a list. result = values.node("", result); } return result; } catch (Backtrack b) { throw b.exception; } } @SuppressWarnings("serial") protected static class Backtrack extends RuntimeException { Throw exception; public Backtrack(Throw exception) { this.exception = exception; } @Override public synchronized Throwable fillInStackTrace() { return this; } } private IValue[] implodeArgs(TypeStore store, Type type, IList args, RascalExecutionContext rex) { int length = args.length(); IValue implodedArgs[] = new IValue[length]; for (int i = 0; i < length; i++) { Type argType = isUntypedNodeType(type) ? type : type.getFieldType(i); implodedArgs[i] = implode(store, argType, (ITree)args.get(i), false, rex); } return implodedArgs; } protected IValue implode(TypeStore store, Type type, IConstructor arg0, boolean splicing, RascalExecutionContext rex) { ITree tree = (ITree) arg0; // always yield if expected type is str, except if regular if (type.isString() && !splicing) { return values.string(TreeAdapter.yield(tree)); } if (SymbolAdapter.isStartSort(TreeAdapter.getType(tree))) { IList args = TreeAdapter.getArgs(tree); ITree before = (ITree) args.get(0); ITree ast = (ITree) args.get(1); ITree after = (ITree) args.get(2); IValue result = implode(store, type, ast, splicing, rex); if (result.getType().isNode()) { IMapWriter comments = values.mapWriter(); comments.putAll((IMap)((INode)result).asAnnotatable().getAnnotation("comments")); IList beforeComments = extractComments(before); if (!beforeComments.isEmpty()) { comments.put(values.integer(-1), beforeComments); } IList afterComments = extractComments(after); if (!afterComments.isEmpty()) { comments.put(values.integer(((INode)result).arity()), afterComments); } result = ((INode)result).asAnnotatable().setAnnotation("comments", comments.done()); } return result; } if (TreeAdapter.isLexical(tree)) { java.lang.String constructorName = unescapedConsName(tree); java.lang.String yield = TreeAdapter.yield(tree); if (constructorName != null) { // make a single argument constructor with yield as argument // if there is a singleton constructor with a str argument if (!type.isAbstractData() && !isUntypedNodeType(type)) { throw RuntimeExceptionFactory.illegalArgument(tree, null, null, "Constructor (" + constructorName + ") should match with abstract data type and not with " + type); } if (isUntypedNodeType(type)) { return values.node(constructorName, values.string(yield)); } Set<Type> conses = findConstructors(type, constructorName, 1, store); Iterator<Type> iter = conses.iterator(); while (iter.hasNext()) { try { @SuppressWarnings("unused") Type cons = iter.next(); ISourceLocation loc = TreeAdapter.getLocation(tree); IConstructor ast = makeConstructor(store, type, constructorName, values.string(yield)); return ast.asAnnotatable().setAnnotation("location", loc); } catch (Backtrack b) { continue; } } throw new Backtrack(RuntimeExceptionFactory.illegalArgument(tree, null, null, "Cannot find a constructor " + type)); } if (type.isInteger()) { return values.integer(yield); } if (type.isReal()) { return values.real(yield); } if (type.isBool()) { if (yield.equals("true")) { return values.bool(true); } if (yield.equals("false")) { return values.bool(false); } throw new Backtrack(RuntimeExceptionFactory.illegalArgument(tree, null, null, "Bool type does not match with " + yield)); } if (type.isString() || isUntypedNodeType(type)) { // NB: in "node space" all lexicals become strings return values.string(yield); } throw RuntimeExceptionFactory.illegalArgument(tree, null, null, "Missing lexical constructor"); } //Set implementation added here by Jurgen at 19/07/12 16:45 if (TreeAdapter.isList(tree)) { if (type.isList() || splicing || isUntypedNodeType(type)) { // if in node space, we also make a list; // NB: this breaks type safety if the top-level tree // is itself a list. Type elementType = type; if (!splicing && !isUntypedNodeType(type)) { elementType = type.getElementType(); } IListWriter w = values.listWriter(); for (IValue arg: TreeAdapter.getListASTArgs(tree)) { w.append(implode(store, elementType, (ITree) arg, false, rex)); } return w.done(); } else if (type.isSet()) { Type elementType = splicing ? type : type.getElementType(); ISetWriter w = values.setWriter(); for (IValue arg: TreeAdapter.getListASTArgs(tree)) { w.insert(implode(store, elementType, (ITree) arg, false, rex)); } return w.done(); } else { throw new Backtrack(RuntimeExceptionFactory.illegalArgument(tree, null, null, "Cannot match list with " + type)); } } //Changes end here if (TreeAdapter.isOpt(tree) && type.isBool()) { IList args = TreeAdapter.getArgs(tree); if (args.isEmpty()) { return values.bool(false); } return values.bool(true); } if (TreeAdapter.isOpt(tree)) { if (!type.isList() && !isUntypedNodeType(type)) { throw new Backtrack(RuntimeExceptionFactory.illegalArgument(tree, null, null, "Optional should match with a list and not " + type)); } Type elementType = isUntypedNodeType(type) ? type : type.getElementType(); IListWriter w = values.listWriter(); for (IValue arg: TreeAdapter.getASTArgs(tree)) { IValue implodedArg = implode(store, elementType, (ITree) arg, true, rex); if (implodedArg instanceof IList) { // splicing for (IValue nextArg: (IList)implodedArg) { w.append(nextArg); } } else { w.append(implodedArg); } // opts should have one argument (if any at all) break; } return w.done(); } if (TreeAdapter.isAmb(tree)) { if (!type.isSet()) { throw new Backtrack(RuntimeExceptionFactory.illegalArgument(tree, null, null, "Ambiguous node should match with set and not " + type)); } Type elementType = type.getElementType(); ISetWriter w = values.setWriter(); for (IValue arg: TreeAdapter.getAlternatives(tree)) { w.insert(implode(store, elementType, (ITree) arg, false, rex)); } return w.done(); } if (ProductionAdapter.hasAttribute(TreeAdapter.getProduction(tree), RascalValueFactory.Attribute_Bracket)) { return implode(store, type, (ITree) TreeAdapter.getASTArgs(tree).get(0), false, rex); } if (TreeAdapter.isAppl(tree)) { IList args = TreeAdapter.getASTArgs(tree); int j = 0; IMapWriter cw = values.mapWriter(); IListWriter aw = values.listWriter(); for (IValue kid : TreeAdapter.getArgs(tree)) { if (TreeAdapter.isLayout((ITree) kid)) { IList cts = extractComments((ITree) kid); if (!cts.isEmpty()) { cw.put(values.integer(j), cts); } j++; } else if (!TreeAdapter.isLiteral((ITree) kid) && !TreeAdapter.isCILiteral((ITree) kid) && !TreeAdapter.isEmpty((ITree) kid)) { aw.append(kid); } } args = aw.done(); int length = args.length(); IMap comments = cw.done(); // // this could be optimized. // i = 0; // int length = args.length(); // while (i < length) { // if (TreeAdapter.isEmpty((IConstructor) args.get(i))) { // length--; // args = args.delete(i); // } // else { // i++; // } // } java.lang.String constructorName = unescapedConsName(tree); if (constructorName == null) { if (length == 1) { // jump over injection return implode(store, type, (ITree) args.get(0), splicing, rex); } // make a tuple if we're in node space if (isUntypedNodeType(type)) { return values.tuple(implodeArgs(store, type, args, rex)); } if (!type.isTuple()) { throw new Backtrack(RuntimeExceptionFactory.illegalArgument(tree, null, null, "Constructor does not match with " + type)); } if (length != type.getArity()) { throw new Backtrack(RuntimeExceptionFactory.arityMismatch(type.getArity(), length, null, null)); } return values.tuple(implodeArgs(store, type, args, rex)); } // if in node space, make untyped nodes if (isUntypedNodeType(type)) { INode ast = values.node(constructorName, implodeArgs(store, type, args, rex)); return ast.asAnnotatable().setAnnotation("location", TreeAdapter.getLocation(tree)).asAnnotatable().setAnnotation("comments", comments); } // make a typed constructor if (!type.isAbstractData()) { throw new Backtrack(RuntimeExceptionFactory.illegalArgument(tree, null, null, "Constructor (" + constructorName + ") should match with abstract data type and not with " + type)); } Set<Type> conses = findConstructors(type, constructorName, length, store); Iterator<Type> iter = conses.iterator(); while (iter.hasNext()) { try { Type cons = iter.next(); ISourceLocation loc = TreeAdapter.getLocation(tree); IValue[] implodedArgs = implodeArgs(store, cons, args, rex); IConstructor ast = makeConstructor(store, type, constructorName, implodedArgs); return ast.asAnnotatable().setAnnotation("location", loc).asAnnotatable().setAnnotation("comments", comments); } catch (Backtrack b) { continue; } } } throw new Backtrack(RuntimeExceptionFactory.illegalArgument(tree, null, null, "Cannot find a constructor for " + type)); } private IList extractComments(IConstructor layout) { final IListWriter comments = values.listWriter(); TreeVisitor<RuntimeException> visitor = new TreeVisitor<RuntimeException>() { @Override public ITree visitTreeAppl(ITree arg) { if (TreeAdapter.isComment(arg)) { comments.append(values.string(TreeAdapter.yield(arg))); } else { for (IValue t: TreeAdapter.getArgs(arg)) { t.accept(this); } } return arg; } @Override public ITree visitTreeAmb(ITree arg) { return arg; } @Override public ITree visitTreeChar(ITree arg) { return arg; } @Override public ITree visitTreeCycle(ITree arg) { return arg; } }; layout.accept(visitor); return comments.done(); } protected boolean isUntypedNodeType(Type type) { return (type.isNode() && !type.isConstructor() && !type.isAbstractData()) || type.isTop(); } /*** end of implode ***/ }