/* ZaubertrankTemplate.java * * Copyright (c) 2001, Ted Nelson and Tuomas Lukka * Copyright (c) 2001, Benja Fallenstein * * You may use and distribute under the terms of either the GNU Lesser * General Public License, either version 2 of the license or, * at your choice, any later version. Alternatively, you may use and * distribute under the terms of the XPL. * * See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of * the licenses. * * This software 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 README * file for more details. * */ /* * Written by Benja Fallenstein */ package org.gzigzag.clang; import org.gzigzag.*; import java.awt.*; /** A module interface to the Archimedes Procedural Layer executor/evaluator. */ public class ZaubertrankTemplate { String rcsid = "$Id: ZaubertrankTemplate.java,v 1.1 2002/10/07 12:37:35 tuukkah Exp $"; public static boolean dbg = false; static final void p(String s) { if(dbg) System.out.println(s); } static final void pa(String s) { System.out.println(s); } /** On the zaubertrank text rank, find a clone of the given parameter. * This is used to obtain the cell on the zaubertrank text rank * corresponding to a given root template parameter cell. */ static public ZZCell findParam(ZZCell root, ZZCell par) { return root.intersect("d.zt-text", 1, par.getRootclone(), "d.clone", 1); } /** Move to the next/previous parameter cell on the zaubertrank text rank. * A parameter cell is one that does not contain text directly, but * rather refers to one of the callables parameters (cloning a cell from * the root template). Given a cell on a zaubertrank text rank, we move * poswards or negwards until we find a parameter cell. *
* Note: Parameter cells are distinguished from text cells by parameter
* cells being clones and text cells being not.
* @returns null
if no parameter cell was found in that
* direction.
*/
static public ZZCell nextParam(ZZCell par, int dir) {
ZZCell c = par;
for(c = c.s("d.zt-text", dir); true; c = c.s("d.zt-text", dir)) {
if(c == null) return null;
if(c.equals(par)) return null; // circular rank!
if(!c.equals(c.getRootclone())) break;
}
return c;
}
/** Return the first or last zaubertrank param cell attached to root.
* Whether the first or last param is returned depends on dir. This
* is used to move "into" an expression, so that if you know the
* expression, you can go to its first/last parameter.
*/
static public ZZCell lastParam(ZZCell root, int dir) {
ZZCell c = root.h("d.zt-text", dir);
if(dir > 0 && !c.equals(c.getRootclone())) return c;
return nextParam(c, -dir);
}
/** A static class representing a virtual cursor into the tree structure.
* At every time, this accurses a parameter cell of an expression.
* If you have a tree: A(B(C, D), E(F)), and start accursing A, then moving
* poswards will give you in order: B, C, C, D, D, B, E, F, F, E, A. Note
* that you traverse each cell twice (A the first time when you set the
* cursor to it).
*
* Note that the "more intuitive" structure C, D, B, F, E, A (RPN) can be * build on top of this, by writing a wrapper. */ static public final class TreeCursor { public VirtualEditCursor cur; public Expression e; // the expression; may be null public ZZCell par; // the parameter of the expression we're at public int side = -1; public TreeCursor(VirtualEditCursor cur) { this.cur = cur; read(); } /** Read the current state in from the virtual edit cursor. */ public void read() { par = cur.get(); try { e = new Expression(cur.peek().h("d.expression")); } catch(java.util.EmptyStackException exc) { e = null; } } /** Move in. * @param side Start at first (-1) or last (+1) parameter? */ public ZZCell moveIn(int side) { ZZCell ehead = par.h("d.expression", true), zpar = null; if(ehead != null) zpar = lastParam(ehead.getRootclone(), side); // zpar can be null now if the subexpression has no params if(zpar != null) { // move in e = new Expression(ehead); ZZCell rootpar = zpar.getRootclone(); par = ParamTemplate.rootToExpression(rootpar, e); cur.push(par); side = side; return par; } else return null; } public ZZCell moveOut() { if(e == null) // we are at the root element of the tree return null; // move out: that's much nicer than before virtual edit cursors ;) cur.pop(); read(); return par; } /** Move left/right. */ public ZZCell moveSidewards(int dir) { final int fwd = dir, back = -dir; if(e == null) // we are at the root element of the tree return null; ZZCell rootpar = ParamTemplate.expressionToRoot(par, e); ZZCell zpar = findParam(e.main.getRootclone(), rootpar); zpar = nextParam(zpar, fwd); if(zpar != null) { // move left/right rootpar = zpar.getRootclone(); par = ParamTemplate.rootToExpression(rootpar, e); cur.set(par); side = back; return par; } else return null; } /** Move in the specified direction and return the cell we reach. * If we hit the end, return null and do not change the * state of the tree cursor. */ public ZZCell move(int dir) { // This method works symmetrically, thus instead of thinking about // posward and negward, we think about "forward" and "backward". // Forward is the direction we're moving to. final int fwd = dir < 0 ? -1 : 1; // forward direction final int back = -fwd; // backward direction // if we're at the back, move into this branch; if we are at // the front, move to the next branch if(side == back) { // move in, or change sides if that's not possible if(moveIn(back) == null) // change sides side = fwd; } else { // move left/right, or move out if that's not possible if(moveSidewards(fwd) == null) if(moveOut() == null) return null; } return par; } /** Move to the next position whose side is negward. * With this method, you can traverse the tree visiting each element * only once, i.e. A(B(C, D), E(F)) ==> A, B, C, D, E, F. It simply * applies move() until it gets a negative side or the end of the * traversal stream; in the latter case, it returns null. * XXX take this out again if it proves not to be used */ public ZZCell moveToNeg(int dir) { ZZCell c = move(dir); LoopDetector d = new LoopDetector(); while(side > 0) { if(c == null) return null; d.detect(c); c = move(dir); } return c; } /** Move in the given direction, until an undefined parameter is found. * If there is no undefined parameter, move as far as possible and * return null. * @returns The undefined parameter, or null when the end of the * stream is reached. */ public ZZCell moveToNextUndefined(int dir) { ZZCell c = move(dir); /** Two loop detectors so that seeing a cell from both sides * doesn't trigger an InfiniteLoopException. */ LoopDetector dp = new LoopDetector(), dn = new LoopDetector(); while(c != null) { if(side > 0) dp.detect(c); else dn.detect(c); if(c.t().equals("") && c.s("d.expression", -1) == null) return c; c = move(dir); } return null; // nothing found, end of stream: move() returned null } } }