/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.shell.syntax; import org.jnode.nanoxml.XMLElement; /** * A RepeatedSyntax instance specifies that a given 'child' syntax may be repeated * a number of times as determined by the constructor arguments. These allow you * to specify a minimum and/or maximum bound on the number of repetitions, and to * specify whether the syntax is 'eager' (i.e. matching as many instances as possible) or * 'lazy' (i.e. matching as few instances as possible). * * @author crawley@jnode.org */ public class RepeatSyntax extends GroupSyntax { private final Syntax child; private final int minCount; private final int maxCount; private final boolean eager; /** * Construct syntax with caller-specified repetition count range and a label. * * @param label this Syntax's label * @param child the child Syntax that may be repeated. * @param minCount the minimum number of occurrences required. * @param maxCount the maximum number of occurrences allowed. * @param eager if {@code true}, the syntax matches as many child instances * as possible, subject to the 'maxCount' constraint. * @param description the description for this syntax */ public RepeatSyntax(String label, Syntax child, int minCount, int maxCount, boolean eager, String description) { super(label, description, child); if (minCount < 0 || maxCount < minCount) { throw new IllegalArgumentException("bad min/max counts"); } this.child = child; this.minCount = minCount; this.maxCount = maxCount; this.eager = eager; } /** * Construct syntax with caller-specified repetition count range and a label. * * @param label this Syntax's label * @param child the child Syntax that may be repeated. * @param minCount the minimum number of occurrences required. * @param maxCount the maximum number of occurrences allowed. */ public RepeatSyntax(String label, Syntax child, int minCount, int maxCount) { this(label, child, minCount, maxCount, false, null); } /** * Construct syntax with caller-specified repetition count range. * * @param child the child Syntax that may be repeated. * @param minCount the minimum number of occurrences required. * @param maxCount the maximum number of occurrences allowed. */ public RepeatSyntax(Syntax child, int minCount, int maxCount) { this(null, child, minCount, maxCount, false, null); } /** * Construct syntax which can repeated from zero to many times. * * @param child the child Syntax that may be repeated. */ public RepeatSyntax(Syntax child) { this(null, child, 0, Integer.MAX_VALUE, false, null); } @Override public String toString() { return "RepeatedSyntax{" + super.toString() + "minCount=" + minCount + ", maxCount=" + maxCount + "}"; } @Override public MuSyntax prepare(ArgumentBundle bundle) { // The result of 'prepare' is rather ugly for the unusual cases. One // alternative would be to add new MuSyntax constructs, and that would // make the Mu parsing code more complicated. Another might be to // create a special Argument class that would operate as a counter. String label = MuSyntax.genLabel(); MuSyntax childSyntax = child.prepare(bundle); MuSyntax res, tail; if (maxCount == Integer.MAX_VALUE) { if (eager) { tail = new MuAlternation(label, new MuSequence(childSyntax, new MuBackReference(label)), null); } else { tail = new MuAlternation(label, null, new MuSequence(childSyntax, new MuBackReference(label))); } } else { int tailCount = maxCount - minCount; tail = null; while (tailCount-- > 0) { if (eager) { tail = new MuAlternation( (tail == null) ? childSyntax : new MuSequence(childSyntax, tail), null); } else { tail = new MuAlternation( (MuSyntax) null, (tail == null) ? childSyntax : new MuSequence(childSyntax, tail)); } } } if (minCount == 0) { res = tail; } else if (minCount == 1) { res = (tail == null) ? childSyntax : new MuSequence(childSyntax, tail); } else { MuSyntax[] sequence = new MuSyntax[minCount]; for (int i = 0; i < minCount; i++) { sequence[i] = childSyntax; } res = (tail == null) ? new MuSequence(sequence) : new MuSequence(new MuSequence(sequence), tail); } if (maxCount == Integer.MAX_VALUE) { res.resolveBackReferences(); } return res; } @Override public String format(ArgumentBundle bundle) { if (minCount == 0) { if (maxCount == Integer.MAX_VALUE) { return "[ " + child.format(bundle) + " ... ]"; } else if (maxCount == 1) { return "[ " + child.format(bundle) + "]"; } else { return "[ " + child.format(bundle) + " ..." + maxCount + " ]"; } } else if (minCount == 1) { if (maxCount == Integer.MAX_VALUE) { return child.format(bundle) + " ..."; } else if (maxCount == 1) { return child.format(bundle); } else { return child.format(bundle) + " ..." + maxCount; } } else { if (maxCount == Integer.MAX_VALUE) { return child.format(bundle) + " " + minCount + "..."; } else { return child.format(bundle) + " " + minCount + "..." + maxCount; } } } @Override public XMLElement toXML() { XMLElement element = basicElement("repeat"); if (minCount > 0) { element.setAttribute("minCount", minCount); } if (maxCount != Integer.MAX_VALUE) { element.setAttribute("maxCount", maxCount); } if (eager) { element.setAttribute("eager", "true"); } return element; } }