/* This program 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 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/>. */ package org.opentripplanner.routing.automata; import java.util.Arrays; import java.util.List; /** * An abstract superclass for all nonterminals, which are models for instantiated NFAs. Also * provides expression building methods which can be used via a static import. * * Thanks to Matt Might, whose self-described "toy Java library" inspired the use of Java syntax to * build up automata: * http://matt.might.net/articles/implementation-of-nfas-and-regular-expressions-in-java/ * * His library also contained a comment suggesting the more functional style provided here by the * build() method. * * @author abyrd */ public abstract class Nonterminal { /** * Recursively builds a new graph of automaton states (an NFA) based on the model provided by * this Nonterminal. This allows nonterminals to be reused in multiple expressions, or multiple * times within a single expression. * * Because we allow epsilon moves in NFAs, every nonterminal can have a single entry and exit * point (i.e. start and accept state). * * @param entry - the start state for the automaton to build * @return the accept state of the newly constructed automaton */ public abstract AutomatonState build(AutomatonState in); /* postfix (instance) expression builder methods */ public Nonterminal star() { return new NTKleeneStar(this); } public Nonterminal plus() { return new NTKleenePlus(this); } public Nonterminal or(Object that) { return new NTChoice(this, wrap0(that)); } public Nonterminal chain(Object... objects) { List<Object> newObjects = Arrays.asList((Object) this); newObjects.addAll(Arrays.asList(objects)); return new NTSequence(wrap(newObjects.toArray())); } /* prefix (factory) expression builder methods (use via static import) */ public static Nonterminal seq(Object... objs) { return new NTSequence(wrap(objs)); } public static Nonterminal star(Object... objs) { return new NTKleeneStar(seq(objs)); } public static Nonterminal plus(Object... objs) { return new NTKleenePlus(seq(objs)); } public static Nonterminal choice(Object... objs) { return new NTChoice(wrap(objs)); } /* wrap terminals in trivial nonterminals to provide them with/to expression builder methods */ private static Nonterminal wrap0(Object o) { if (o instanceof Integer) return new NTTrivial((Integer) o); else if (o instanceof Nonterminal) return (Nonterminal) o; else throw new RuntimeException( "attempted to build an NFA out of something that was not a terminal or a nonterminal"); } private static Nonterminal[] wrap(Object... objects) { Nonterminal[] nonterminals = new Nonterminal[objects.length]; int i = 0; for (Object object : objects) { nonterminals[i++] = wrap0(object); } return nonterminals; } /* build automata from expressions */ public NFA toNFA() { return new NFA(this); } public DFA toDFA() { return new DFA(this); } }