/* * Vitry, copyright (C) Hans Hoglund 2011 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * See COPYING.txt for details. */ package vitry.runtime; import static vitry.runtime.VitryRuntime.*; import static vitry.runtime.struct.Seqs.*; import vitry.runtime.StandardFunction.*; import vitry.runtime.error.*; import vitry.runtime.parse.*; import vitry.runtime.struct.*; /** * Combined interface and factory for syntax-tree rewriting. * * @author Hans Hoglund */ abstract public class Rewriting { private static final Rewriting TOP_LEVEL_REWRITER = new TopLevelRewriter(); private static final Rewriting DELAY_REWRITER = new DelayRewriter(); static final Symbol _ = Symbol.intern("_"); static final Symbol Fn = Symbol.intern("Fn"); static final Symbol Left = Symbol.intern("Left"); static final Symbol Apply = Symbol.intern("Apply"); static final Symbol __delay = Symbol.intern("__delay"); abstract public Seq<?> rewrite(Seq<Pattern> seq); public static Rewriting delayRewriter() { return DELAY_REWRITER; } public static final Rewriting topLevelRewriter() { return TOP_LEVEL_REWRITER; } public static final Rewriting opsRewriter(Env<Symbol, Fixity> fix, Env<Symbol, Symbol> ctxt) { return new OpRewriter(fix, ctxt); } } /** * Rewrites * delay M -> __delay (fn (_) M) */ class DelayRewriter extends Rewriting { public Seq<?> rewrite(Seq<Pattern> seq) { return productOf(Apply, __delay, productOf(Fn, productOf(Left, _), seq.head())); } } /** * Rewrites * a = M -> a = M * a b = M -> a = (fn (b) M) * a b c = M -> a = (fn (b c) M) * ... */ class TopLevelRewriter extends Rewriting { public Seq<?> rewrite(Seq<Pattern> seq) { if (isAssign(seq)) { Pattern assign = seq.head(); Pattern leftSeq = seq.tail().head(); Pattern right = seq.tail().tail().head(); if (isApply(second((Seq<Pattern>) leftSeq))) { Pattern left = first((Seq<Pattern>)leftSeq); Seq<Pattern> applySeq = (Seq<Pattern>) second((Seq<Pattern>) leftSeq); Pattern apply = applySeq.head(); Pattern name = applySeq.tail().head(); Product params = product(applySeq.tail().tail()); params = params.mapProduct(new StandardFunction.Unary() { public Object apply(Object a) throws InvocationError { Object res = product(Seqs.from(Left,(Pattern) a)); return res; } }); Product r = product(single(right)); Pattern val = product(concat(params.prepend(Fn), r)); return product(Seqs.from(assign, product(Seqs.from(left, name)), val)); } } return seq; } boolean isAssign(Object o) { if (o instanceof Seq) { Object h = ((Seq<?>) o).head(); return Parsing.getTokenType(h) == VitryParser.Assign; } return false; } boolean isApply(Object o) { if (o instanceof Seq) { Object h = ((Seq<?>) o).head(); return Parsing.getTokenType(h) == VitryParser.Apply; } return false; } } /** * Rewrites opererator-form syntax trees into application-form. */ class OpRewriter extends Rewriting { final Env<Symbol, Fixity> fixities; final Env<Symbol, Symbol> context; final Symbol delimiter; public OpRewriter(Env<Symbol, Fixity> fixities, Env<Symbol, Symbol> context) { this.fixities = fixities; this.context = context; this.delimiter = context.lookup(Context.DELIMITER); } /** * Rewrite the given sequence of operator trees into a single * expression. The sequence may be headed by a non-optree expression. * * An operator tree is an expression such as (+ 2 ...) where + is any * operator. * * This algorithm proceeds by locating the highest-precedence * operator tree and unify it with its predecessor. If several elements * compete for highest precedence, the leftmost or rightmost expression * is selected, based on the associativity of the operator in question. * * The first element is never considered as it always represents a * non-optree expression or a unary operator tree. * * Returns an (Apply, +, ...) expression. */ public Seq<Pattern> rewrite(Seq<Pattern> seq) { /* * If the expression is in normal form, convert it to application * form and return */ if (length(seq) == 1) { return rewriteAsApplication((Product) seq.head()); } /* * Find the element of highest precedence. * * Also store its direct predecessor element, all other preceding elements, * and all following elements. */ Fixity fix = null; Product before = null; Pattern pred = null; Pattern prim = null; Product after = null; SeqIterator<Pattern> it = iterate(seq); int max = -1; for (Pattern now = null, last = null; it.hasNext();) { last = now; now = it.next(); /* * Skip expressions that are not operator trees. * This may include already normalized expressions. * * Always skip the first value. */ if (last == null || !isOpTree(now)) continue; fix = getFixity(now); if (fix != null && (fix.getPrecedence() > max || (fix.getPrecedence() == max && !fix.getAssociativity()))) { prim = (Product) now; pred = last; after = product(it.following()); max = fix.getPrecedence(); } } if (prim == null || fix == null) throw new ParseError("Correputed operator tree " + seq); /* * Find preceding elements by backtracking */ before = product(pred == null ? null : untilElement(seq, pred)); /* * So we have * (before ++ (pred) ++ (prim) ++ after) == seq * * Now unify pred and prim, then reapply (before ++ (unify) ++ after). */ Product unify; if (hasSameOperator(prim, pred) && fix.isGathering()) { /* * Simply concatenate */ unify = product(concat((Product) pred, ((Product) prim).tail())); } else if (isOpTree(pred) && isShallow(pred) && length((Product) pred) <= 2) { /* * Both preceding and primary are unprocessed * Hoist and reinsert */ Pattern hoist = last((Product) pred); unify = product(concat (init((Product) pred), product(single ((Pattern) insert(hoist, (Product) prim))))); } else { /* * Primary is processed or non-op expression * Simply insert */ unify = insert(pred, (Product) prim); } return rewrite(product(concat(before, cons((Pattern) unify, after)))); } /** * If the given value is list on the form (Op, _), return the fixity of the operator. * Otherwise return null. */ Fixity getFixity(Pattern p) { if (p instanceof Seq) { return getFixity((Product) p); } return null; } Fixity getFixity(Product p) { return fixities.lookup(Interpreter.evalOperator(p.head(), delimiter)); } /** * Matches sequences of length 2 or greater whose first * element is an operator. */ static boolean isOpTree(Pattern p) { if (p instanceof Seq) { Seq<?> s = (Seq<?>) p; if (length(s) < 2) return false; if (!isOperator(first(s))) return false; return true; } return false; } /** * Matches any sequence s where all elements in s are not * headed by an operator. */ static boolean isShallow(Pattern p) { if (p instanceof Seq) { return foldl(new Unary() { public Object apply(Object a, Object b) throws InvocationError { if (!(Boolean) a) { return false; } if (b instanceof Seq) { return !isOperator(first((Seq<?>) b)); } return true; } }, true, (tail((Seq<?>) p))); } return false; } /** * Matches op tokens. */ static boolean isOperator(Object o) { // TODO accept symbols parsing as operators if (o instanceof Pattern) { return Parsing.getTokenType((Pattern) o) == VitryParser.Op; } return false; } /** * Returns whether two sequences have the same head operator. */ static boolean hasSameOperator(Object xs, Object ys) { if (xs instanceof Seq && ys instanceof Seq) { Object x = ((Seq<?>) xs).head(); Object y = ((Seq<?>) ys).head(); // TODO verify op type return x.toString().equals(y.toString()); } return false; } static Product insert(Pattern hoist, Product primary) { return product(cons(primary.head(), cons(hoist, primary.tail()))); } /** * Deep-walks a sequence of patterns and/or sequences and replaces * all elements (+,...) with (Apply,+,...). */ static Product rewriteAsApplication(Product seq) { if (!isOperator(first(seq))) { return seq; } Seq<Pattern> map = seq.map(new Unary() { public Object apply(Object s) throws InvocationError { if (s instanceof Seq) { return rewriteAsApplication((Product) s); } else { return s; } } }); return product(cons(Apply, map)); } }