private void matchBoundListVar(IList previousBinding) { if (debug) { System.err.println("matchBoundListVar: " + previousBinding); } assert isListVar[patternCursor]; int start = listVarStart[patternCursor]; int length = listVarLength[patternCursor]; for (int i = 0; i < previousBinding.length(); i += delta) { if (debug) System.err.println( "comparing: " + previousBinding.get(i) + " and " + listSubject.get(subjectCursor + i)); if (!previousBinding.get(i).isEqual(listSubject.get(subjectCursor + i))) { forward = false; listVarLength[patternCursor] = 0; patternCursor -= delta; if (debug) System.err.println("child fails"); return; } } subjectCursor = start + length; if (debug) System.err.println("child matches, subjectCursor=" + subjectCursor); patternCursor += delta; }
private IList makeSubList(int start, int length) { assert isListVar[patternCursor]; // TODO: wrap concrete lists in an appl(list( )) again. if (start > subjectSize) return listSubject.sublist(0, 0); return listSubject.sublist(start, length); }
@Override public <U extends IValue, V extends IValue> Result<U> fieldUpdate( String name, Result<V> repl, TypeStore store) { IConstructor tree = getValue(); if (TreeAdapter.isAppl(tree)) { int found = -1; IConstructor foundType = null; IConstructor prod = TreeAdapter.getProduction(tree); IList syms = ProductionAdapter.getSymbols(prod); // TODO: find deeper into optionals, alternatives and sequences checking the actual arguments // for presence/absence of optional trees. for (int i = 0; i < syms.length(); i++) { IConstructor sym = (IConstructor) syms.get(i); if (SymbolAdapter.isLabel(sym)) { if (SymbolAdapter.getLabel(sym).equals(name)) { found = i; foundType = SymbolAdapter.delabel(sym); break; } } } if (found != -1) { Type nont = RascalTypeFactory.getInstance().nonTerminalType(foundType); if (repl.getType().isSubtypeOf(nont)) { IList args = TreeAdapter.getArgs(tree).put(found, repl.getValue()); return makeResult(getType(), tree.set("args", args), ctx); } throw new UnexpectedType(nont, repl.getType(), ctx.getCurrentAST()); } if (Factory.Tree_Appl.hasField(name)) { Type fieldType = Factory.Tree_Appl.getFieldType(name); if (repl.getType().isSubtypeOf(fieldType)) { throw new UnsupportedOperation( "changing " + name + " in concrete tree", ctx.getCurrentAST()); } throw new UnexpectedType(fieldType, repl.getType(), ctx.getCurrentAST()); } throw RuntimeExceptionFactory.noSuchField(name, ctx.getCurrentAST(), ctx.getStackTrace()); } throw new UnsupportedOperation("field update", ctx.getCurrentAST()); }
@Override public <U extends IValue> Result<U> fieldAccess(String name, TypeStore store) { IConstructor tree = getValue(); if (TreeAdapter.isAppl(tree)) { int found = -1; IConstructor foundType = null; IConstructor prod = TreeAdapter.getProduction(tree); if (!ProductionAdapter.isRegular(prod)) { IList syms = ProductionAdapter.getSymbols(prod); // TODO: find deeper into optionals, checking the actual arguments for presence/absence of // optional trees. for (int i = 0; i < syms.length(); i++) { IConstructor sym = (IConstructor) syms.get(i); while (SymbolAdapter.isConditional(sym)) { sym = SymbolAdapter.getSymbol(sym); } if (SymbolAdapter.isLabel(sym)) { if (SymbolAdapter.getLabel(sym).equals(name)) { found = i; foundType = SymbolAdapter.delabel(sym); } } } if (found != -1) { Type nont = RascalTypeFactory.getInstance().nonTerminalType(foundType); IValue child = TreeAdapter.getArgs(tree).get(found); return makeResult(nont, child, ctx); } } } if (tree.getConstructorType().hasField(name)) { return makeResult(tree.getConstructorType().getFieldType(name), tree.get(name), ctx); } throw RuntimeExceptionFactory.noSuchField(name, ctx.getCurrentAST(), ctx.getStackTrace()); }
/* * We are positioned in the pattern at a list variable and match it with * the current subject starting at the current position. * On success, the cursors are advanced. * On failure, switch to backtracking (forward = false) mode. */ private void matchBindingListVar(IMatchingResult child) { assert isListVar[patternCursor]; int start = listVarStart[patternCursor]; int length = listVarLength[patternCursor]; // int reducedLength = (length == 0) ? 0 : ((length < delta) ? 1 : Math.max(length - delta + 1, // 0)); // round to nearest unskipped element int reducedLength = (length <= 1) ? length : (length - (length - 1) % delta); if (debug) { System.err.println("length=" + length); System.err.println("reducedLength=" + reducedLength); } IList sublist = makeSubList(start, reducedLength); if (debug) System.err.println( "matchBindingListVar: init child #" + patternCursor + " (" + child + ") with " + sublist); // TODO : check if we can use a static type here!? child.initMatch(ResultFactory.makeResult(sublist.getType(), sublist, ctx)); if (child.next()) { subjectCursor = start + length; if (debug) System.err.println("child matches, subjectCursor=" + subjectCursor); patternCursor += delta; } else { forward = false; listVarLength[patternCursor] = 0; patternCursor -= delta; if (debug) System.err.println("child fails, subjectCursor=" + subjectCursor); } }
public Object loadPT(IConstructor[] fileRef, String sourcePath, String sourceName) { IConstructor file = fileRef[0]; ISourceLocation loc = TreeAdapter.getLocation(file); int lineNumber = loc.getBeginLine(); Object ret = null; Var.pushThreadBindings( RT.map( LOADER, RT.makeClassLoader(), SOURCE_PATH, sourcePath, SOURCE, sourceName, METHOD, null, LOCAL_ENV, null, LOOP_LOCALS, null, NEXT_LOCAL_NUM, 0, RT.CURRENT_NS, RT.CURRENT_NS.deref(), LINE_BEFORE, lineNumber, LINE_AFTER, lineNumber, RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref(), RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref(), RT.DATA_READERS, RT.DATA_READERS.deref())); UPTRLispReader reader = new UPTRLispReader(vf, errors); try { if (TreeAdapter.isAmb(file)) { System.err.println("Amb"); } // File is start[File], so IConstructor file2 = (IConstructor) TreeAdapter.getArgs(file).get(1); IList args = TreeAdapter.getArgs(file2); // Probably only this is the list of forms; don't forget to fix // below. IList forms = TreeAdapter.getArgs((IConstructor) args.get(0)); IListWriter newArgs = vf.listWriter(); for (int i = 0; i < forms.length(); i++) { IConstructor form = (IConstructor) forms.get(i); // only forms, no literals at this level. UPTRLispReader.Pair p = reader.read(form); newArgs.append(p.tree); LINE_AFTER.set(TreeAdapter.getLocation(form).getEndLine()); ret = eval(p.obj, false); LINE_BEFORE.set(TreeAdapter.getLocation(form).getBeginLine()); if (i < forms.length() - 2) { i++; newArgs.append(forms.get(i)); // layout } } // Fix tree file2 = file2.set("args", newArgs.done()); file = file.set( "args", vf.list(TreeAdapter.getArgs(file).get(0), file2, TreeAdapter.getArgs(file).get(2))); } catch (UPTRLispReader.ReaderException e) { throw new CompilerException(sourcePath, e.line, e.getCause()); } finally { Var.popThreadBindings(); } fileRef[0] = file; return ret; }
@Override protected Result<IBool> equalToConcreteSyntax(ConcreteSyntaxResult that) { IConstructor left = this.getValue(); IConstructor right = that.getValue(); if (TreeAdapter.isLayout(left) && TreeAdapter.isLayout(right)) { return bool(true, ctx); } if (TreeAdapter.isAppl(left) && TreeAdapter.isAppl(right)) { IConstructor p1 = TreeAdapter.getProduction(left); IConstructor p2 = TreeAdapter.getProduction(right); if (!p1.isEqual(p2)) { return bool(false, ctx); } IList l1 = TreeAdapter.getArgs(left); IList l2 = TreeAdapter.getArgs(right); if (l1.length() != l2.length()) { return bool(false, ctx); } for (int i = 0; i < l1.length(); i++) { IValue kid1 = l1.get(i); IValue kid2 = l2.get(i); // Recurse here on kids to reuse layout handling etc. Result<IBool> result = makeResult(kid1.getType(), kid1, ctx).equals(makeResult(kid2.getType(), kid2, ctx)); if (!result.getValue().getValue()) { return bool(false, ctx); } if (TreeAdapter.isContextFree(left)) { i++; // skip layout } } return bool(true, ctx); } if (TreeAdapter.isChar(left) && TreeAdapter.isChar(right)) { return bool((TreeAdapter.getCharacter(left) == TreeAdapter.getCharacter(right)), ctx); } if (TreeAdapter.isAmb(left) && TreeAdapter.isAmb(right)) { ISet alts1 = TreeAdapter.getAlternatives(left); ISet alts2 = TreeAdapter.getAlternatives(right); if (alts1.size() != alts2.size()) { return bool(false, ctx); } // TODO: this is very inefficient again: for (IValue alt1 : alts1) { for (IValue alt2 : alts2) { Result<IBool> result = makeResult(alt1.getType(), alt1, ctx).equals(makeResult(alt2.getType(), alt2, ctx)); if (result.getValue().getValue()) { // As soon an alt1 is equal to an alt2 // continue the outer loop. continue again; } } // If an alt1 is not equal to any of the the alt2's return false; return bool(false, ctx); } return bool(true, ctx); } return bool(false, ctx); }
public void write(IValue rel, ISourceLocation loc, IEvaluatorContext ctx) { OutputStream out = null; Type paramType = ctx.getCurrentEnvt().getTypeBindings().get(types.parameterType("T")); if (!paramType.isRelation() && !paramType.isListRelation()) { throw RuntimeExceptionFactory.illegalTypeArgument( "A relation type is required instead of " + paramType, ctx.getCurrentAST(), ctx.getStackTrace()); } try { boolean isListRel = rel instanceof IList; out = ctx.getResolverRegistry().getOutputStream(loc.getURI(), false); ISet irel = null; IList lrel = null; if (isListRel) { lrel = (IList) rel; } else { irel = (ISet) rel; } int nfields = isListRel ? lrel.asRelation().arity() : irel.asRelation().arity(); if (header) { for (int i = 0; i < nfields; i++) { if (i > 0) out.write(separator); String label = paramType.getFieldName(i); if (label == null || label.isEmpty()) label = "field" + i; writeString(out, label); } out.write('\n'); } String separatorAsString = new String(Character.toChars(separator)); for (IValue v : (isListRel ? lrel : irel)) { ITuple tup = (ITuple) v; int sep = 0; for (IValue w : tup) { if (sep == 0) sep = separator; else out.write(sep); if (w.getType().isString()) { String s = ((IString) w).getValue(); if (s.contains(separatorAsString) || s.contains("\n") || s.contains("\r") || s.contains("\"")) { s = s.replaceAll("\"", "\"\""); out.write('"'); writeString(out, s); out.write('"'); } else writeString(out, s); } else { writeString(out, w.toString()); } } out.write('\n'); } out.flush(); out.close(); } catch (IOException e) { throw RuntimeExceptionFactory.io(values.string(e.getMessage()), null, null); } finally { if (out != null) { try { out.flush(); out.close(); } catch (IOException ioex) { throw RuntimeExceptionFactory.io(values.string(ioex.getMessage()), null, null); } } } }
/* * Perform a list match. When forward=true we move to the right in the pattern * and try to match the corresponding elements of the subject. When the end of the pattern * and the subject are reaching, match returns true. * * When a non-matching element is encountered, we switch to moving to the left (forward==false) * and try to find alternative options in list variables. * * When the left-hand side of the pattern is reached while moving to the left, match return false, * and no more options are available: hasNext() will return false. * * @see org.meta_environment.rascal.interpreter.MatchPattern#match() */ @Override public boolean next() { if (debug) System.err.println("List.next: entering"); checkInitialized(); if (debug) System.err.println("AbstractPatternList.match: " + subject); if (!hasNext) return false; forward = firstMatch; firstMatch = false; do { /* * Determine the various termination conditions. */ if (debug) System.err.println( "List.do: patternCursor=" + patternCursor + ", subjectCursor=" + subjectCursor); if (forward) { if (patternCursor >= patternSize) { if (subjectCursor >= subjectSize) { if (debug) System.err.println(">>> match returns true"); // hasNext = (subjectCursor > subjectSize) && (patternCursor > patternSize); // // JURGEN GUESSES THIS COULD BE RIGHT return true; } forward = false; patternCursor -= delta; } } else { if (patternCursor >= patternSize) { patternCursor -= delta; subjectCursor -= delta; // Ok? } } if (patternCursor < 0 || subjectCursor < 0) { hasNext = false; if (debug) System.err.println( ">>> match returns false: patternCursor=" + patternCursor + ", forward=" + forward + ", subjectCursor=" + subjectCursor); return false; } /* * Perform actions for the current pattern element */ IMatchingResult child = patternChildren.get(patternCursor); if (debug) { System.err.println(this); System.err.println( "loop: patternCursor=" + patternCursor + ", forward=" + forward + ", subjectCursor= " + subjectCursor + ", child=" + child + ", isListVar=" + isListVar[patternCursor] + ", class=" + child.getClass()); } /* * A binding occurrence of a list variable */ if (isListVar[patternCursor] && isBindingVar[patternCursor]) { if (forward) { listVarStart[patternCursor] = subjectCursor; if (patternCursor == patternSize - 1) { if (debug) { System.err.println("subjectSize=" + subjectSize); System.err.println("subjectCursor=" + subjectCursor); } listVarLength[patternCursor] = Math.max(subjectSize - subjectCursor, 0); } else { listVarLength[patternCursor] = listVarMinLength[patternCursor]; } } else { listVarLength[patternCursor] += delta; forward = true; } if (debug) System.err.println( "list var: start: " + listVarStart[patternCursor] + ", len=" + listVarLength[patternCursor] + ", minlen=" + listVarMinLength[patternCursor] + ", maxlen=" + listVarMaxLength[patternCursor]); if (listVarLength[patternCursor] > listVarMaxLength[patternCursor] || listVarStart[patternCursor] + listVarLength[patternCursor] >= subjectSize + delta) { subjectCursor = listVarStart[patternCursor]; if (debug) System.err.println("Length failure, subjectCursor=" + subjectCursor); forward = false; listVarLength[patternCursor] = 0; patternCursor -= delta; } else { matchBindingListVar(child); } /* * Reference to a previously defined list variable */ } else if (isListVar[patternCursor] && !isBindingVar[patternCursor] && ctx.getCurrentEnvt().getVariable(varName[patternCursor]).getType().isList()) { if (forward) { listVarStart[patternCursor] = subjectCursor; Result<IValue> varRes = ctx.getCurrentEnvt().getVariable(varName[patternCursor]); IValue varVal = varRes.getValue(); if (varRes.getType().isList()) { assert varVal != null && varVal.getType().isList(); int varLength = ((IList) varVal).length(); listVarLength[patternCursor] = varLength; if (subjectCursor + varLength > subjectSize) { forward = false; patternCursor -= delta; } else { matchBoundListVar((IList) varVal); } } } else { subjectCursor = listVarStart[patternCursor]; patternCursor -= delta; } /* * Any other element of the pattern */ } else { if (forward && subjectCursor < subjectSize) { if (debug) System.err.println( "AbstractPatternList.match: init child " + patternCursor + " with " + listSubject.get(subjectCursor)); IValue childValue = listSubject.get(subjectCursor); // TODO: check if we can use a static type here?! child.initMatch(ResultFactory.makeResult(childValue.getType(), childValue, ctx)); if (child.next()) { subjectCursor += delta; patternCursor += delta; if (debug) System.err.println( "AbstractPatternList.match: child matches, subjectCursor=" + subjectCursor); } else { forward = false; // why is here no subjectCursor -= delta; needed? patternCursor -= delta; } } else { if (subjectCursor < subjectSize && child.next()) { if (debug) System.err.println("child has next:" + child); forward = true; subjectCursor += delta; patternCursor += delta; } else { if (debug) System.err.println("child has no next:" + child); forward = false; subjectCursor -= delta; patternCursor -= delta; } } } } while (true); }
@Override public void initMatch(Result<IValue> subject) { if (debug) { System.err.println("List: initMatch: subject=" + subject); } // This is an experiment to add abstract list matching for concrete subjects // if (subject.getType().isSubtypeOf(Factory.Tree)) { // IConstructor tree = (IConstructor) subject.getValue(); // if (TreeAdapter.isList(tree)) { // subject = ResultFactory.makeResult(Factory.Args, TreeAdapter.getArgs(tree), ctx); // IConstructor rhs = TreeAdapter.getType(tree); // // if (SymbolAdapter.isIterPlusSeps(rhs) || SymbolAdapter.isIterStarSeps(rhs)) { // this.delta = SymbolAdapter.getSeparators(rhs).length() + 1; // } // } // else { // hasNext = false; // return; // } // } super.initMatch(subject); if (!subject.getValue().getType().isList()) { hasNext = false; return; } listSubject = (IList) subject.getValue(); listSubjectType = listSubject.getType(); staticListSubjectType = subject.getType(); staticListSubjectElementType = staticListSubjectType.isList() ? subject.getType().getElementType() : tf.valueType(); subjectCursor = 0; patternCursor = 0; subjectSize = ((IList) subject.getValue()).length(); reducedSubjectSize = (subjectSize + delta - 1) / delta; if (debug) { System.err.println("reducedPatternSize=" + reducedPatternSize); System.err.println("reducedSubjectSize=" + reducedSubjectSize); } isListVar = new boolean[patternSize]; isBindingVar = new boolean[patternSize]; varName = new String[patternSize]; allVars = new HashSet<String>(); listVarStart = new int[patternSize]; listVarLength = new int[patternSize]; listVarMinLength = new int[patternSize]; listVarMaxLength = new int[patternSize]; listVarOccurrences = new int[patternSize]; int nListVar = 0; /* * Pass #1: determine the list variables */ for (int i = 0; i < patternSize; i += delta) { IMatchingResult child = patternChildren.get(i); isListVar[i] = false; isBindingVar[i] = false; Environment env = ctx.getCurrentEnvt(); if (child instanceof TypedMultiVariablePattern) { TypedMultiVariablePattern tmvVar = (TypedMultiVariablePattern) child; Type tmvType = tmvVar.getType(env, null); String name = tmvVar.getName(); varName[i] = name; isListVar[i] = true; listVarOccurrences[i] = 1; ++nListVar; if (!tmvVar.isAnonymous() && allVars.contains(name)) { throw new RedeclaredVariable(name, getAST()); } else if (tmvType.comparable(listSubject.getType().getElementType()) || (tmvVar.bindingInstance() && tmvType.comparable(listSubject.getType()))) { tmvVar.convertToListType(); if (!tmvVar.isAnonymous()) { allVars.add(name); } isBindingVar[i] = true; } else { hasNext = false; return; } } else if (child instanceof MultiVariablePattern) { MultiVariablePattern multiVar = (MultiVariablePattern) child; String name = multiVar.getName(); varName[i] = name; isListVar[i] = true; nListVar++; if (!multiVar.isAnonymous() && allVars.contains(name)) { isBindingVar[i] = false; } else if (multiVar.isAnonymous()) { isBindingVar[i] = true; } else { allVars.add(name); Result<IValue> varRes = env.getVariable(name); if (varRes == null || multiVar.bindingInstance()) { isBindingVar[i] = true; } else { isBindingVar[i] = false; Type varType = varRes.getType(); if (isAnyListType(varType)) { if (!varType.comparable(listSubjectType)) { hasNext = false; return; } } else { if (!(varType instanceof NonTerminalType) && !(varType.comparable(staticListSubjectElementType))) { hasNext = false; return; } } } } } else if (child instanceof ConcreteListVariablePattern) { ConcreteListVariablePattern listVar = (ConcreteListVariablePattern) child; String name = listVar.getName(); varName[i] = name; isListVar[i] = true; if (!listVar.isAnonymous()) allVars.add(name); isBindingVar[i] = true; listVarOccurrences[i] = 1; nListVar++; } else if (child instanceof QualifiedNamePattern) { QualifiedNamePattern qualName = (QualifiedNamePattern) child; String name = qualName.getName(); varName[i] = name; if (!qualName.isAnonymous() && allVars.contains(name)) { /* * A variable that was declared earlier in the pattern */ isListVar[i] = true; nListVar++; listVarOccurrences[i]++; } else if (qualName.isAnonymous()) { /* * Nothing to do */ } else { Result<IValue> varRes = env.getVariable(name); if (varRes == null || qualName.bindingInstance()) { // A completely new non-list variable, nothing to do } else { Type varType = varRes.getType(); if (isAnyListType(varType)) { /* * A variable declared in the current scope. */ if (varType.comparable(listSubjectType)) { isListVar[i] = true; isBindingVar[i] = varRes.getValue() == null; nListVar++; } else { hasNext = false; return; } } else { if (varType instanceof NonTerminalType) { // suppress comparable test for Nonterminal types // TODO: this should be done better } else if (!varType.comparable(staticListSubjectElementType)) { hasNext = false; return; } } } } } else if (child instanceof VariableBecomesPattern) { // Nothing to do } else { if (debug) { System.err.println("List: child " + child); System.err.println("List: child is a" + child.getClass()); } Type childType = child.getType(env, null); // TODO: pattern matching should be specialized such that matching appl(prod...)'s does not // need to use list matching on the fixed arity children of the application of a production if (!(childType instanceof NonTerminalType) && !childType.comparable(staticListSubjectElementType)) { hasNext = false; return; } java.util.List<IVarPattern> childVars = child.getVariables(); if (!childVars.isEmpty()) { for (IVarPattern vp : childVars) { // TODO: This does not profit from extra information allVars.add(vp.name()); } isListVar[i] = false; nListVar++; } } } /* * Pass #2: assign minimum and maximum length to each list variable */ for (int i = 0; i < patternSize; i += delta) { if (isListVar[i]) { // TODO: reduce max length according to number of occurrences listVarMaxLength[i] = delta * Math.max(reducedSubjectSize - (reducedPatternSize - nListVar), 0); listVarLength[i] = 0; listVarMinLength[i] = delta * ((nListVar == 1) ? Math.max(reducedSubjectSize - reducedPatternSize - 1, 0) : 0); if (debug) { System.err.println( "listvar " + i + " min= " + listVarMinLength[i] + " max=" + listVarMaxLength[i]); } } } firstMatch = true; hasNext = subject.getValue().getType().isList() && reducedSubjectSize >= reducedPatternSize - nListVar; if (debug) { System.err.println("List: hasNext=" + hasNext); } }