Java tutorial
// InterpreterKleeneVisitor.java // // The Kleene Programming Language // Copyright 2006-2012 SAP AG // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Author: ken.beesley@sap.com (Kenneth R. Beesley) // [short description here] // An interpreter for the Kleene language based on a Java // finite-state library defined in FSLib (the current FSLib is a // Java wrapper of the OpenFst library). This class deals with the // AST trees coming from the Kleene parser and the interpreter // stack. It calls methods in the Java wrapping of the // finite-state library (currently based on OpenFst) . import java.util.Stack; import com.ibm.icu.text.UTF16; import com.ibm.icu.text.UCharacterIterator; // see htj UCharacterIterator import java.io.*; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.io.SAXReader; import org.dom4j.Element; import org.dom4j.Attribute; import org.dom4j.ElementPath; import org.dom4j.ElementHandler; import java.util.HashSet; import java.util.Set; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import com.sun.syndication.io.XmlReader; import org.apache.commons.lang3.ObjectUtils; //import org.apache.commons.lang.StringEscapeUtils ; import com.ibm.icu.text.Transliterator; // local StreamFlusher class class StreamFlusher extends Thread { InputStream is; String type; StreamFlusher(InputStream is, String type) { this.is = is; this.type = type; } public void run() { try { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) System.out.println(type + ">" + line); } catch (IOException ioe) { ioe.printStackTrace(); } } } // If necessary, move enums to a separate Enum.java file. // These enums are used when compiling alternation rules. enum RuleArrowType { LEFT, RIGHT; } enum RuleObligType { OBLIG, OPT; } enum RuleMatchType { ALL, MAX_L2R, MIN_L2R, MAX_R2L, MIN_R2L; } enum RuleContextLevels { ONE, TWO; } enum RuleMapType { MAP, MARKUP; } // temporarily hold the ASTs in an ASTrule_lhs_markup class MarkupParts { private ASTleft_markup_insertion leftMarkupInsertion; private ASTright_markup_insertion rightMarkupInsertion; // constructor public MarkupParts() { leftMarkupInsertion = null; rightMarkupInsertion = null; } void setLeftMarkupInsertion(ASTleft_markup_insertion leftMarkupInsertion) { this.leftMarkupInsertion = leftMarkupInsertion; } void setRightMarkupInsertion(ASTright_markup_insertion rightMarkupInsertion) { this.rightMarkupInsertion = rightMarkupInsertion; } ASTleft_markup_insertion getLeftMarkupInsertion() { return leftMarkupInsertion; } ASTright_markup_insertion getRightMarkupInsertion() { return rightMarkupInsertion; } } // local RuleLocalVarSetting class // Used in interpretation of 'where' clauses in alternation rules. // e.g. $a -> $b / left _ right { where $a _E_ $@{b, d, g}, $b _E_ $@{p, t, k} // Associates a local rule variable, e.g. $a, and an Fst (N.B. not as AST). // This where clause would result in three compilation scenarios with local variables: // 1. $a = b, $b = p // 2. $a = d, $b = t // 3. $a = g, $b = k // In this example, the three compilation scenarios each involves // two RuleLocalVarSetting objects. // // The where_... clause contains one or more ASTelmt_of_net_list_exp daughters // each of which contains an ASTnet_id, and an ASTnet_list_exp. // Note that the ASTnet_list_exp might be any net_list_exp, and so could // be a net_list_id whose elements are already compiled into Fsts. So we // don't have the option of storing ASTs here. // // So for each $a = net_list_exp // the Fst values of net_list_exp are those in the current Frame. // class RuleLocalVarSetting { String varName; Fst fstValue; // constructor public RuleLocalVarSetting() { varName = null; fstValue = null; } public RuleLocalVarSetting(String varName, Fst fstValue) { this.varName = varName; this.fstValue = fstValue; } void setVarName(String varName) { this.varName = varName; } void setFstValue(Fst fstValue) { this.fstValue = fstValue; } String getVarName() { return varName; } Fst getFstValue() { return fstValue; } } // Holds the left and right parts of a single context, // extracted from a rule AST. // (One syntactic rule can have multiple contexts.) // Each syntactic context can be one_level or two_level. class RuleContextSyntacticParts { private RuleContextLevels levels; // value is RuleContextLevels.ONE or .TWO // either one or both contexts can be null private ASTleft_rule_context leftRuleContext; private ASTright_rule_context rightRuleContext; // constructors public RuleContextSyntacticParts() { this.leftRuleContext = null; this.rightRuleContext = null; } public RuleContextSyntacticParts(RuleContextLevels levels) { this.levels = levels; this.leftRuleContext = null; this.rightRuleContext = null; } // end constructors void setLevels(RuleContextLevels levels) { this.levels = levels; } void setLeftRuleContext(ASTleft_rule_context leftRuleContext) { this.leftRuleContext = leftRuleContext; } void setRightRuleContext(ASTright_rule_context rightRuleContext) { this.rightRuleContext = rightRuleContext; } RuleContextLevels getLevels() { return levels; } ASTleft_rule_context getLeftRuleContext() { return leftRuleContext; } ASTright_rule_context getRightRuleContext() { return rightRuleContext; } } // one RuleSyntacticParts is created for each rule AST // from one RuleSyntacticParts object, an ArrayList of // RuleSemanticParts objects is created. A one-to-many // mapping (from one RuleSyntacticParts to RuleSemanticParts) // can result if the rule has where classes or epenthesis. class RuleSyntacticParts { private RuleArrowType arrowType; // RuleArrowType.LEFT or .RIGHT private RuleObligType obligType; // RuleObligType.OBLIG or .OPT private RuleMatchType matchType; // RuleMatchType.ALL, .MAX_L2R, // .MIN_L2R, .MAX_R2L, .MIN_R2L private RuleMapType mapType; // RuleMapType.MAP or .MARKUP // A straightforward mapping rule always has LHS upper and lower ASTs, // usually set by a constructor. Both set for a straightforward // mapping rule. Only one set for a markup rule. See constructors below. private ASTrule_lhs_upper lhsUpper; private ASTrule_lhs_lower lhsLower; // a "transducer" rule has the upper and lower levels already, combined // in a single regexp child that defines an FST private ASTrule_lhs_transducer lhsTransducer; // A markup rule will have exactly one of lhsUpper and lhsLower set to null, // with the following two ASTs set to non-null values private ASTleft_markup_insertion leftMarkupInsertion; private ASTright_markup_insertion rightMarkupInsertion; // for the RHS contexts, if any (the constructors always set it to null) private ArrayList<RuleContextSyntacticParts> contexts; // for the where_clauses, if any (the constructors always set it to null) private ArrayList<ArrayList<RuleLocalVarSetting>> localVarSettings; // constructors // constructor for straightforward mapping rules // a -> b / ... public RuleSyntacticParts(ASTrule_lhs_upper lhsUpper, ASTrule_lhs_lower lhsLower) { // a straightforward rule always has a LHS with upper and lower this.lhsUpper = lhsUpper; this.lhsLower = lhsLower; this.lhsTransducer = null; this.leftMarkupInsertion = null; this.rightMarkupInsertion = null; this.mapType = RuleMapType.MAP; // assume initially that no RHS (contexts) or where_clauses are present contexts = null; localVarSettings = null; } // constructor for transducer rules // a:b -> / ... public RuleSyntacticParts(ASTrule_lhs_transducer lhsTransducer) { this.lhsUpper = null; this.lhsLower = null; this.lhsTransducer = lhsTransducer; this.leftMarkupInsertion = null; this.rightMarkupInsertion = null; this.mapType = RuleMapType.MAP; // assume initially that no RHS (contexts) or where_clauses are present contexts = null; localVarSettings = null; } // constructor for right-arrow markup rules X -> Y ... Z // (transducer rules cannot be Markup rules) public RuleSyntacticParts(ASTrule_lhs_upper lhsUpper, ASTleft_markup_insertion leftMarkupInsertion, ASTright_markup_insertion rightMarkupInsertion) { this.lhsUpper = lhsUpper; this.lhsLower = null; this.lhsTransducer = null; this.leftMarkupInsertion = leftMarkupInsertion; this.rightMarkupInsertion = rightMarkupInsertion; this.mapType = RuleMapType.MARKUP; // assume initially that no RHS (contexts) or where_clauses are present contexts = null; localVarSettings = null; } // constructor for left-arrow markup rules Y ... Z <- X // (transducer rules cannot be Markup rules) public RuleSyntacticParts(ASTleft_markup_insertion leftMarkupInsertion, ASTright_markup_insertion rightMarkupInsertion, ASTrule_lhs_lower lhsLower) { this.lhsUpper = null; this.lhsLower = lhsLower; this.lhsTransducer = null; this.leftMarkupInsertion = leftMarkupInsertion; this.rightMarkupInsertion = rightMarkupInsertion; this.mapType = RuleMapType.MARKUP; // assume initially that no RHS (contexts) or where_clauses are present contexts = null; localVarSettings = null; } // end constructors // // Start setters void setArrowType(RuleArrowType arrowType) { // .LEFT or .RIGHT this.arrowType = arrowType; } void setObligType(RuleObligType obligType) { // .OBLIG or .OPT this.obligType = obligType; } void setMatchType(RuleMatchType matchType) { // .ALL, .MAX_L2R, .MAX_R2L // .MIN_L2R or .MIN_R2L this.matchType = matchType; } void setMapType(RuleMapType mapType) { // .MAP or .MARKUP this.mapType = mapType; } void setLhs_upper(ASTrule_lhs_upper lhsUpper) { this.lhsUpper = lhsUpper; } void setLhs_lower(ASTrule_lhs_lower lhsLower) { this.lhsLower = lhsLower; } void setLhs_transducer(ASTrule_lhs_transducer lhsTransducer) { this.lhsTransducer = lhsTransducer; } void setContexts(ArrayList<RuleContextSyntacticParts> contexts) { this.contexts = contexts; } void setLocalVarSettings(ArrayList<ArrayList<RuleLocalVarSetting>> localVarSettings) { this.localVarSettings = localVarSettings; } // start getters RuleArrowType getArrowType() { return arrowType; } RuleObligType getObligType() { return obligType; } RuleMatchType getMatchType() { return matchType; } RuleMapType getMapType() { return mapType; } ASTrule_lhs_upper getLhsUpper() { return lhsUpper; } ASTrule_lhs_lower getLhsLower() { return lhsLower; } ASTrule_lhs_transducer getLhsTransducer() { return lhsTransducer; } ASTleft_markup_insertion getLeftMarkupInsertion() { return leftMarkupInsertion; } ASTright_markup_insertion getRightMarkupInsertion() { return rightMarkupInsertion; } ArrayList<RuleContextSyntacticParts> getContexts() { return contexts; } ArrayList<ArrayList<RuleLocalVarSetting>> getLocalVarSettings() { return localVarSettings; } } // interpreter class public class InterpreterKleeneVisitor implements KleeneVisitor { Environment env; // env keeps track of Frames and tables // that map variable names to their values SymMap symmap; // symmap provides a two-way mapping between // alphabetic and multichar symbols and the integers used to store // them on FST arcs; Provide methods // 1. int symmap.putsym(String) // adds it only if not already added // 2. int symmap.getint(String) // returns null if not found // 3. String symmap.getsym(int) // If another library is ever used, make OpenFstLibraryWrapper // implement an interface named LibraryWrapper, and then change // the following line to // LibraryWrapper lib; OpenFstLibraryWrapper lib; Hulden hulden; Frame mainFrame; // corresponds to GUI symbol-table window // Constructor constructor // (called just once--only one interpreter is used in Kleene) public InterpreterKleeneVisitor(Environment e) { env = e; stack = new Stack<Object>(); // only one stack is used symmap = new SymMap(); // see other Constructor options // only one SymMap is used // If another library is ever used, parameterize // which one is loaded here lib = new OpenFstLibraryWrapper(env, symmap); // only one is used hulden = new Hulden(lib, symmap); // only one is used // Mans Hulden's algorithms // add OTHER_ID and OTHER_NONID this way because the representation // of OTHER could change for a new library; do not refer directly // to "OTHER_ID" or "OTHER_NONID" in this file lib.AddOtherId(symmap); lib.AddOtherNonId(symmap); } void outputInterpMessage(String msg, Object data) { if (((InterpData) data).getInGUI()) { PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); terminal.appendToHistory(msg); } else { System.out.println(msg); } } // for OTHER display private void getSigmaStrings(Fst fst, StringBuilder sbhex, StringBuilder sb) { HashSet<Integer> sigma = fst.getSigma(); int cpv; for (Iterator<Integer> iter = sigma.iterator(); iter.hasNext();) { cpv = iter.next().intValue(); sbhex.append(Integer.toString(cpv, 16) + " "); sb.append(symmap.getsym(cpv)); if (iter.hasNext()) { sb.append(", "); } else { sb.append(" "); } } } Fst correctSigmaOther(Fst fst) { lib.CorrectSigmaOtherInPlace(fst); // OTHER should never match the # used in rules KRB ruleany // Rethink: 2015-01-18 perhaps the OTHER in a _rule_ FST should // never match the # //if (fst.getContainsOther()) { // fst.getSigma().add(symmap.putsym(hulden.ruleWordBoundarySym)) ; //} return fst; } Fst interpRestrictionExp(Fst lhs, Fst rhs, boolean forAlternationRule) { // When compiling stand-alone restriction expressions like // a => L _ R // then if fst Two below contains .#. (the ruleWordBoundarySym), // then you need // to compute Three as shown below, as the complement of Two relative to // .#. \.#.* .#. // Two will contain .#. if the restriction expression looks like a => # l _ r // or a => l _ r #, etc. // // But if the restriction is being done as part of an alternation rule // (replace rule) compilation, in the step that computes "Context" as a // restriction expression, you don't have to worry about Two // containing .#., because the boundary symbols are taken care of by the // "Base" fst used when compiling alternation rules. // Here to be safe, need to make sure that lhs does not // contain the restriction delimiter (if it contains OTHER, then // it would normally include the restriction delimiter; so if it // contains OTHER, then add restDelimSym to the sigma if (lhs.getContainsOther()) { if (lhs.getFromSymtab()) { // then work on a copy to avoid corrupting an FST in the // symbol table lhs = lib.CopyFst(lhs); } // then just add the restriction delimiter to the sigma lhs.getSigma().add(symmap.putsym(hulden.restDelimSym)); } // else it has a finite sigma, and is already safe // Hulden: [ \x* x lhs x \x* ] Fst oneMinuend = lib.Concat5Fsts(hulden.NotRestDelimStarFst(), lib.OneArcFst(hulden.restDelimSym), lhs, lib.OneArcFst(hulden.restDelimSym), hulden.NotRestDelimStarFst()); // "One" is the language of all strings that have at least one lhs // in an illegal context. // Hulden: define One [\x* x lhs x \x*] - [\x* [L x \x* x R] \x*] Fst One = lib.Difference(oneMinuend, rhs); Fst Two = One; // Hulden: define Two `[One, x, 0] ; hulden.SubstEpsilonInPlace(Two, hulden.restDelimSym); // why this apparent null operation? here and below? // At one time, at least, it appeared that OpenFst was not // perceiving correctly that Two contained epsilons after the // call to SubstEpsilonInPlace() Two = lib.Concat(Two, lib.EmptyStringLanguageFst()); // KRB changed to OptimizeInPlaceForce from OptimizeInPlace lib.OptimizeInPlaceForce(Two); // Compute Fst Three Fst Three = null; if (forAlternationRule) { // don't need to treat the ruleWordBoundarySym as special, because this // is taken care of by the "Base" fst that is also computed when // compiling alternation rules; here just compute Three as ~Two Three = lib.Complement(Two); } else { // for stand-alone restriction expressions, need to compute Three // specially iff Two contains .#. (ruleWordBoundarySym) if (Two.getSigma().contains(symmap.putsym(hulden.ruleWordBoundarySym))) { // Hulden: then need to // define Three [.#. \.#.* .#.] - Two // (take the complement with respect to .#. \.#.* .#. // so that any .#. in the contexts is constrained // to be on the beginning or end. Three = lib.Difference(lib.Concat3Fsts(lib.OneArcFst(hulden.ruleWordBoundarySym), newNotWordBoundStarFst(), lib.OneArcFst(hulden.ruleWordBoundarySym)), Two); hulden.SubstEpsilonInPlace(Three, hulden.ruleWordBoundarySym); } else { // Two does not contain .#. // just take the normal complement .* - Two Three = lib.Complement(Two); } } Three = lib.Concat(Three, lib.EmptyStringLanguageFst()); // KRB: changed to OptimizeInPlaceForce from OptimizeInPlace lib.OptimizeInPlaceForce(Three); // KRB: consider if # should be excluded from OTHER, as in rule FSTs. // I think not, but keep it in mind. return Three; } Fst interpRestrictionContext(Fst leftContext, Fst rightContext) { // First make sure that leftContext and rightContext do not // contain the restDelimSym (i.e. iff they contain // OTHER, then these contexts DO implicitly contain the restDelimSym; // then add the restDelimSym to the sigma so that OTHER // doesn't cover it) if (leftContext.getContainsOther()) { if (leftContext.getFromSymtab()) { // then work with a copy to avoid changing something // in the symbol table leftContext = lib.CopyFst(leftContext); } leftContext.getSigma().add(symmap.putsym(hulden.restDelimSym)); } if (rightContext.getContainsOther()) { if (rightContext.getFromSymtab()) { rightContext = lib.CopyFst(rightContext); } rightContext.getSigma().add(symmap.putsym(hulden.restDelimSym)); } // each individual context in a restriction expression gets // interpreted as // leftContext __RD [\__RD]* __RD rightContext Fst resultFst = lib.Concat5Fsts(leftContext, lib.OneArcFst(hulden.restDelimSym), hulden.NotRestDelimStarFst(), lib.OneArcFst(hulden.restDelimSym), rightContext); // KRB: changed to OptimizeInPlaceForce from OptimizeInPlace lib.OptimizeInPlaceForce(resultFst); return resultFst; } Fst newNotWordBoundStarFst() { // Hulden \.#.* Fst notWordBoundStar = lib.UniversalLanguageFst(); // now add the ruleWordBoundarySym to the sigma, // so that OTHER here doesn't cover it notWordBoundStar.getSigma().add(symmap.putsym(hulden.ruleWordBoundarySym)); return notWordBoundStar; } int getIntValue(Object obj) { if (obj instanceof Long) { return ((Long) obj).intValue(); } else if (obj instanceof Double) { return ((Double) obj).intValue(); } else { throw new KleeneArgException("Object sent to getIntValue is neither Long nor Double"); } } // ******************************************************* // End of functions written to implement alternation rules // ******************************************************* private static Document parseXML(String str) throws DocumentException { SAXReader reader = new SAXReader(); Document document = reader.read(str); return document; } private String basicNetListInfo(NetList netList) { return "Network list value: Size: " + netList.size(); } private String basicFstInfo(Fst fst) { // Collect basic information about the new Fst for display int stateCount = lib.NumStates(fst); String stateStr = Integer.toString(stateCount); if (stateCount == 1) { stateStr += " state, "; } else { stateStr += " states, "; } int arcCount = lib.NumArcs(fst); String arcStr = Integer.toString(arcCount); if (arcCount == 1) { arcStr += " arc, "; } else { arcStr += " arcs, "; } String pathStr; if (lib.IsCyclic(fst)) { pathStr = "Cyclic, "; } else { long pathCount = lib.NumPaths(fst); pathStr = Long.toString(pathCount); if (pathCount == 1) { pathStr += " path, "; } else { pathStr += " paths, "; } } String arityStr; if (lib.IsAcceptor(fst)) { // looks like an acceptor to OpenFst if (!fst.getContainsOther()) { // simple case, does NOT contain OTHER arityStr = "Acceptor, "; } else if (lib.IsSemanticAcceptor(fst)) { arityStr = "Acceptor, "; } else { arityStr = "Semantic Transducer, "; } } else { // definitely a transducer arityStr = "Transducer, "; } String weightStr; if (lib.IsUnweighted(fst)) { weightStr = "Unweighted, "; } else { weightStr = "Weighted, "; } String otherStr; if (fst.getContainsOther()) { otherStr = "Contains OTHER"; } else { otherStr = "Closed Sigma"; } String sapRtnStr = ""; if (fst.getIsRtn()) { sapRtnStr = ", SAP RTN"; } // GetShortFstInfo() is a library function that uses info-main.h, // 2012-04-19 info-main.h seems to be obsolete. The FstInfo // class is now defined in fst/script/info-impl.h, but the FstInfo // class shouldn't be used directly anymore. // // FstInfo objects; it is possible and might be useful to get much // more info from this FstInfo object. But for a net like // $net = abc ; // the <FstInfo>.NumArcs() reports 4 arcs, which might include the // exit arc, but such a counting did not appear to be consistent String infoString = lib.GetShortFstInfo(fst) + ", " + stateStr + arcStr + pathStr + arityStr + weightStr + otherStr + sapRtnStr; return infoString; } private String getFullpath(String userTyped) { File file = new File(userTyped); String fullpath = ""; if (userTyped.startsWith("~/")) { // rooted at the user's home directory fullpath = System.getProperty("user.home") + userTyped.substring(1); } else { try { fullpath = file.getCanonicalPath(); } catch (IOException ioe) { // KRB--how to handle this? ioe.printStackTrace(); } } return fullpath; } // During the interpretation of the AST, the value of a node is typically // pushed onto the stack Stack<Object> stack; public void setMainFrame() { mainFrame = env.getCurrentFrame(); } // InterpreterKleeneVisitor.jdelete is called from // the finalize() method of Fst.java; only this interpreter // should know about the underlying native C++ library public static void jdelete(long ptr) { OpenFstLibraryWrapper.CppDelete(ptr); return; } public void reset() { stack.clear(); return; } // Limit the number of analysis/generation results shown in the GUI // KRB: magic number int resultDisplayLimit = 100; // parameterize this somehow int generate = 0; int analyze = 1; private void listOutputStrings(String msg, Fst resultFst, Object data) { PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); terminal.appendToHistory("\n" + msg + "\n"); long stringCount = lib.NumPaths(resultFst); if (stringCount == 0) { terminal.appendToHistory("(resulting language is empty)"); } else if (stringCount == -1) { // then has loops, infinite language terminal.appendToHistory("(resulting language is infinite)"); } else if (stringCount <= resultDisplayLimit) { // then just list them (parameterize this figure later) FstStringLister lister = new FstStringLister(terminal, symmap); // second arg 0 is for input side, // 1 for output side lib.ListAllStrings(resultFst, 0, lister); // ptr to fst, 0 for input projection, false for printEpsilons // the projection doesn't matter here, of course, because the // network was already reduced above to the input projection } else { terminal.appendToHistory("(resulting language exceeds resultDisplayLimit: " + resultDisplayLimit + ")"); } } // testAnalyze() is called from the 'test' function (in the GUI) // apply the testFst to one string (an array of CPVs) public void testAnalyze(Fst testFst, int[] cpvArray, Object data) { Fst resultFst = lib.ApplyToOneString(testFst, cpvArray, analyze); env.put("$analysisresult", resultFst); // add an Icon to the Symtab window addToGUISymtab("$analysisresult", SymtabIcons.NET_IMAGE, data); listOutputStrings("Analysis results:", resultFst, data); } // testGenerate() is called from the 'test' function (in the GUI) public void testGenerate(Fst testFst, int[] cpvArray, Object data) { // For comments, see testAnalyze() above Fst resultFst = lib.ApplyToOneString(testFst, cpvArray, generate); env.put("$generationresult", resultFst); // add an Icon to the Symtab window addToGUISymtab("$generationresult", SymtabIcons.NET_IMAGE, data); listOutputStrings("Generation results:", resultFst, data); } void writeXmlHelper(Fst fst, String fullpath, String encoding) { FstXmlWriter fstXmlWriter = new FstXmlWriter(lib.ArcType(fst), fst.getSigma(), symmap, fst.getContainsOther(), new File(fullpath), encoding); // Call Fst2xml to iterate through the native Fst // states and arcs. (I can't do this from Java.) It will make calls // back to methods in the fstXmlWriter to do the actual output // to file. // (The C++ code has iterators, but Unicode file output // from C++ is not // worth the trouble. Even if the C++ code were written to // write the XML // directly, it would still have to make calls back to the // symmap method // .getsym(i) // to convert the int-value labels to strings. lib.Fst2xml(fst, fstXmlWriter, symmap.getStartPuaCpv()); } void writeXmlHelperStateOriented(Fst fst, String fullpath, String name, String encoding) { // see if it has any input/upper epsilon loops boolean ubounded = lib.IsUBounded(fst); FstXmlWriterStateOriented fstXmlWriterStateOriented = new FstXmlWriterStateOriented(lib.ArcType(fst), fst.getSigma(), symmap, fst.getContainsOther(), ubounded, new File(fullpath), name, encoding); // Call Fst2xmlStateOriented to iterate through the native Fst // states and arcs. (I can't do this from Java.) It will make calls // back to methods in the fstXmlWriterStateOriented to do the actual output // to file. // (The C++ code has iterators, but Unicode file output // from C++ is not // worth the trouble. Even if the C++ code were written to // write the XML // directly, it would still have to make calls back to the // symmap method // .getsym(i) // to convert the int-value labels to strings. lib.Fst2xmlStateOriented(fst, fstXmlWriterStateOriented, symmap.getStartPuaCpv()); } private Fst xml2fst(String filepath) throws Exception { // read an XML file representing a network, return the network final Fst fst = lib.EmptyLanguageFst(); final HashSet<Integer> sigma = fst.getSigma(); SAXReader reader = new SAXReader(); // SAXReader from dom4j // each SAXReader handler must define onStart() and onEnd() methods // when the kleeneFst element is first found reader.addHandler("/kleeneFst", new ElementHandler() { public void onStart(ElementPath path) { Element current = path.getCurrent(); // semiring is an attribute on the kleeneFst node String semiring = current.attribute("semiring").getValue(); } public void onEnd(ElementPath path) { } }); reader.addHandler("/kleeneFst/sigma", new ElementHandler() { public void onStart(ElementPath path) { if (path.getCurrent().attribute("containsOther").getValue().equals("true")) { fst.setContainsOther(true); } else { fst.setContainsOther(false); } ; } public void onEnd(ElementPath path) { } }); reader.addHandler("/kleeneFst/sigma/sym", new ElementHandler() { public void onStart(ElementPath path) { } public void onEnd(ElementPath path) { Element sym = path.getCurrent(); sigma.add(symmap.putsym(sym.getText())); sym.detach(); } }); // when the arcs element is first found reader.addHandler("/kleeneFst/arcs", new ElementHandler() { public void onStart(ElementPath path) { // grab the two attrs and convert to int int startState = Integer.parseInt(path.getCurrent().attribute("start").getValue()); int numStates = Integer.parseInt(path.getCurrent().attribute("numStates").getValue()); lib.AddStates(fst, numStates); // native function, add this many // states to the new Fst lib.SetStart(fst, startState); // set the start state } public void onEnd(ElementPath path) { } }); // handle each whole arc element reader.addHandler("/kleeneFst/arcs/arc", new ElementHandler() { // in an ElementHandler, need to supply .onStart(), // called when the start tag is found, and // .onEnd(), which is called when the end tag is found. public void onStart(ElementPath path) { } public void onEnd(ElementPath path) { // retrieve the entire arc element Element arc = path.getCurrent(); // these two are always present int src_id = Integer.parseInt(arc.attribute("s").getValue()); int dest_id = Integer.parseInt(arc.attribute("d").getValue()); // there will be either one io attr xor separate i and o attrs // (keep the io option to facilitate hand-written XML files) String input; String output; Attribute io = arc.attribute("io"); if (io != null) { input = io.getValue(); output = io.getValue(); } else { input = arc.attribute("i").getValue(); output = arc.attribute("o").getValue(); } if (!symmap.containsKey(input)) { // symbol name in XML file not in the // current internal symtab symmap.putsym(input); } if (!symmap.containsKey(output)) { symmap.putsym(output); } // the w attr is optional in the arc elmt Attribute w = arc.attribute("w"); if (w != null) { // call AddArc to add an arc to the fst // being built from the XML file description // semiring generalization point lib.AddArc(fst, src_id, symmap.getint(input), symmap.getint(output), Float.parseFloat(w.getValue()), dest_id); } else { // semiring generalization point lib.AddArcNeutralWeight(fst, src_id, symmap.getint(input), symmap.getint(output), dest_id); } arc.detach(); } }); // for each full final element reader.addHandler("/kleeneFst/arcs/final", new ElementHandler() { public void onStart(ElementPath path) { } public void onEnd(ElementPath path) { Element arc = path.getCurrent(); // s attr is always present int src_id = Integer.parseInt(arc.attribute("s").getValue()); // the w attr is optional Attribute w = arc.attribute("w"); if (w != null) { lib.SetFinal(fst, src_id, Float.parseFloat(w.getValue())); } else { lib.SetFinalNeutralWeight(fst, src_id); } arc.detach(); } }); Document document = null; // the actual XML reading/parsing is done here try { // XmlReader detects the encoding of the XML document and // handles BOMs, including the UTF-8 BOMs that Java usually // chokes on document = reader.read(new XmlReader(new FileInputStream(filepath))); // Old, pre-XmlReader code //if (encoding.equals("UTF-8")) { // // then need to work around SUN's irresponsible decision not to // // handle the optional UTF-8 BOM correctly // document = reader.read(new InputStreamReader( // new UTF8BOMStripperInputStream(new FileInputStream(filepath)), // "UTF-8") // ) ; //} else { // document = reader.read(new InputStreamReader( // new FileInputStream(filepath), // encoding) // ) ; //} } catch (DocumentException de) { // dom4j DocumentException extends Exception de.printStackTrace(); throw de; } catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); throw fnfe; } catch (Exception e) { e.printStackTrace(); throw e; } correctSigmaOther(fst); return fst; } // KRB: refine the type checking; perhaps store the return type // somehow in the FuncValue object private boolean typeMatch(String id, Object obj) { // check longer prefixes first if (id.startsWith("$^^") && obj instanceof FuncValue) return true; else if (id.startsWith("$^") && obj instanceof FuncValue) return true; else if (id.startsWith("$@^^") && obj instanceof FuncValue) return true; else if (id.startsWith("$@^") && obj instanceof FuncValue) return true; else if (id.startsWith("$@") && obj instanceof NetList) return true; else if (id.startsWith("$") && obj instanceof Fst) return true; else if (id.startsWith("#^^") && obj instanceof FuncValue) return true; else if (id.startsWith("#^") && obj instanceof FuncValue) return true; else if (id.startsWith("#@^^") && obj instanceof FuncValue) return true; else if (id.startsWith("#@^") && obj instanceof FuncValue) return true; else if (id.startsWith("#@") && obj instanceof NumList) return true; // void and voidvoid functions else if (id.startsWith("^^") && obj instanceof FuncValue) return true; else if (id.startsWith("^") && obj instanceof FuncValue) return true; else if (id.startsWith("#") && ((obj instanceof Long) || (obj instanceof Double))) return true; else return false; } private int getParamIndex(String id, ArrayList<ParamSlot> pal) { int index = 0; for (Iterator<ParamSlot> iter = pal.iterator(); iter.hasNext();) { ParamSlot ps = iter.next(); if (ps.getName().equals(id)) { return index; } index++; } return -1; // not found } // bind parameters params in a function call private void bind_params(ArrayList<ParamSlot> pal, ArgCounts ac, Object data) { // The pal (param array list) comes from the FuncObject itself, indicating // the parameters that the function expects. // The ArgCounts object was created when the arg_list was evaluated, and it // contains positional_arg_count and named_arg_count int positional_args_count = ac.getPositionalArgsCount(); int named_args_count = ac.getNamedArgsCount(); // the arguments should be on the stack; // values (corresponding to positional args) on top // with NamedArgs (corresponding to named args) underneath, // so all the arguments are popped off the stack in their original // syntactic order. int param_count = pal.size(); // the number of params to bind // for each positional argument, there must be a corresponding // required or optional parameter (with or without a default value); // First assign params left-to-right from all positional arguments. if (positional_args_count > param_count) { throw new FuncCallException( "Function called with more positional arguments than there are parameters."); } // Loop through the positional_args, if any, and bind the // corresponding ParamSlots l->r in the pal. The positional-arg values // are on the stack, pushed on in reverse syntactic order, so they // can be popped off in the original syntactic order. for (int i = 0; i < positional_args_count; i++) { // get the i-th ParamSlot ParamSlot ps = pal.get(i); // pop the corresponding argument value off the stack Object obj = stack.pop(); if (typeMatch(ps.getName(), obj)) { ps.setValue(obj); } else { throw new FuncCallException("Attempt to set arg named " + ps.getName() + " with a positional argument of the incorrect type."); } } // Now handle the named arguments for (int i = 0; i < named_args_count; i++) { // Each named arg is represented as a NamedArg object on the stack. NamedArg na = (NamedArg) stack.pop(); String id = na.getName(); // Find the parameter with that id int ind = getParamIndex(id, pal); if (ind == -1) { throw new FuncCallException("Attempt to set non-existent param named " + id + "."); } ParamSlot ps = pal.get(ind); if (ps.hasValue()) { // was already set by a positional argument throw new FuncCallException( "Attempt to set param " + id + " twice, with both a positional and a named argument."); } // no type problem if the names matched; // set the optional parameter value from the named argument ps.setValue(na.getValue()); } // now pass through the ArrayList making sure that all Param Slots // have been set, one way or another. for (int i = 0; i < param_count; i++) { ParamSlot ps = pal.get(i); if (!ps.hasValue()) { if (ps.hasDefault()) { ps.setValueToDefault(); } else { throw new FuncCallException( "Param " + ps.getName() + " not set by either a positional or a named argument."); } } } // Finally, actually bind the param names to the arg values, // in the current Frame for (int i = 0; i < param_count; i++) { ParamSlot ps = pal.get(i); env.put(ps.getName(), ps.getValue()); } } private void addToGUISymtab(String id, String iconFileName, Object data) { ((InterpData) data).getGUI().addSymtabIconButton(id, iconFileName); } private void removeFromGUISymtab(String id, Object data) { ((InterpData) data).getGUI().removeSymtabIconButton(id); } private boolean isOpenFstRtnConventions() { if (((Long) env.get("#KLEENErtnConventions")) .longValue() == ((Long) env.get("#KLEENEopenFstRtnConventions")).longValue()) { return true; } else { return false; } } // End of helper functions /************** visit methods for an AST produced by the parser ****** * * in classic Visitor fashion, the root of the AST is sent a message to * "accept" this Visitor * **********************************************************************/ // the method for SimpleNode should never be called, // but for some reason it seems to be required public Object visit(SimpleNode node, Object data) { System.out.println("Error: call to visit(SimpleNode) method in InterpreterKleeneVisitor"); return data; } public Object visit(ASTprogram node, Object data) { // Not currently used? Scripts are parsed and executed // one statement at a time? node.childrenAccept(this, data); return data; } public Object visit(ASTstand_alone_block node, Object data) { // these blocks are groups of statements, evaluated in // a new Frame env.allocateFrame(); int childCount = node.jjtGetNumChildren(); for (int i = 0; i < childCount; i++) { try { node.jjtGetChild(i).jjtAccept(this, data); if (((InterpData) data).getLoopContinue() || ((InterpData) data).getLoopBreak() || ((InterpData) data).getFuncReturn() || ((InterpData) data).getQuitSession()) { // bail out of this block, but don't reset // the fields in data break; } } catch (RuntimeException re) { env.releaseFrame(); throw re; } } env.releaseFrame(); return data; } public Object visit(ASTif_else_block node, Object data) { // these blocks seen in if-else statements // see ASTloop_block for the blocks in while and until // see ASTfunc_block for the blocks seen in function definitions // see ASTstand_alone_block for block-grouped statements int childCount = node.jjtGetNumChildren(); for (int i = 0; i < childCount; i++) { node.jjtGetChild(i).jjtAccept(this, data); if (((InterpData) data).getLoopContinue() || ((InterpData) data).getLoopBreak() || ((InterpData) data).getFuncReturn() || ((InterpData) data).getQuitSession()) { // bail out of this if-then block, but don't reset // the fields in data break; } } return data; } public Object visit(ASTloop_block node, Object data) { // these blocks seen in while and until statements // see ASTblock for the blocks in if-then // see ASTfunc_block for the blocks seen in function // definitions int childCount = node.jjtGetNumChildren(); for (int i = 0; i < childCount; i++) { node.jjtGetChild(i).jjtAccept(this, data); if (((InterpData) data).getLoopContinue() || ((InterpData) data).getLoopBreak() || ((InterpData) data).getFuncReturn() || ((InterpData) data).getQuitSession()) { // bail out of this if-then block, but don't reset // the fields in data break; } } return data; } public Object visit(ASTfunc_block node, Object data) { int childCount = node.jjtGetNumChildren(); for (int i = 0; i < childCount; i++) { node.jjtGetChild(i).jjtAccept(this, data); // there shouldn't be break or next statements immediately // in such a block (syntactic? or semantic error?) // if it hit a return stmt during the execution of this stmt // (at any depth), then .getFuncReturn() will be true if (((InterpData) data).getFuncReturn()) { ((InterpData) data).setFuncReturn(false); break; } else if (((InterpData) data).getQuitSession()) { // don't reset the data field here break; } } return data; } public Object visit(ASTsap_rtn_conventions_statement node, Object data) { Long l = (Long) env.get("#KLEENEsapRtnConventions"); // use putGlobal() to cause the binding to be made in the // global symbol table, so it will be available when // processing functions defined in .kleene/global/predefined.kl env.putGlobal("#KLEENErtnConventions", env.get("#KLEENEsapRtnConventions")); outputInterpMessage("// Setting SapRtnConventions", data); return data; } public Object visit(ASTcontinue_statement node, Object data) { // "return" the information that a 'continue' command was // encountered, in the data ((InterpData) data).setLoopContinue(true); return data; } public Object visit(ASTbreak_statement node, Object data) { // "return" the information that a 'break' command was // encountered, in the data ((InterpData) data).setLoopBreak(true); return data; } public Object visit(ASTquit_statement node, Object data) { // "return" the information that a 'quit' command was // encountered, in the data ((InterpData) data).setQuitSession(true); return data; } public Object visit(ASTempty_statement node, Object data) { // parser found an isolated semicolon // no-op return data; } public Object visit(ASTnet_assignment node, Object data) { // Syntax: $net = a*b+(dog|cat)s? ; // two daughters: // net_id // regexp // Don't evaluate the zeroth daughter (which would return // a network value; rather just get the image of this LHS String net_id = ((ASTnet_id) node.jjtGetChild(0)).getImage(); // If net_id is already bound in the current symtab, either to a // normal object or to an ExternValue (containing a handle to the // non-local Frame where net_id is bound), then this assignment will // just reset the existing binding. // Otherwise, if there is no local // binding of net_id at all, this assignment statement is intended // to both declare a new _local_ net_id key and to bind it to the // value of the right-hand-side in the current local symtab. // Problem: if the right-hand-side refers to (i.e. retrieves the // value of) net_id as a free (i.e. non-local) variable, then the // result is confusing and usually an error. // So when, on the RHS, a variable is free (not found in the // currentFrame) // then the currentFrame is given a placeholder binding of // key->FreeVariable, // to block any subsequent attempt to give key a real local binding in // currentFrame node.jjtGetChild(1).jjtAccept(this, data); // Should leave an Fst object on the stack (see Fst.java) Fst fst = (Fst) (stack.pop()); // relate the net_id and the fst in the current Frame env.put(net_id, fst); // Now if the current Frame is the main Frame, add // an appropriate SymbolIconButton to the window. In other // words, do NOT try to add a SymbolIconButton if the // current Frame is a temporary one used to process a // function call, or a stand_alone_block, // and do NOT try to add a SymbolIconButton // if the interpreter is working on a startup script (i.e. // is not operating within the interactive GUI) if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { // then we're in the GUI // add an Icon to the Symtab window addToGUISymtab(net_id, SymtabIcons.NET_IMAGE, data); PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); terminal.appendToHistory("// " + basicFstInfo(fst)); } return data; } public Object visit(ASTnet_id node, Object data) { // Called only when the net_id is on the RHS (_not_ the LHS) // of an assignment statement (i.e. inside a regular // expression or right-linear production); the value of a net_id // like $mynet is an Fst String net_id = node.getImage(); // with the String name, retrieve the Fst from the environment Fst fst = (Fst) env.get(net_id); if (fst != null) { // The net_id was found in a symbol table. // // Fst is a Java wrapper object around a long int that stores a // (basically C++) pointer to a network. Need to mark this // Fst as being "fromSymtab" if, as here, the value came from a // symbol table (and therefore cannot be changed, i.e. must be // persistent in case the net_id is referred to again). fst.setFromSymtab(true); // leave the Fst on the stack stack.push(fst); } else { // attempt to refer to (use) an undefined variable throw new UndefinedIdException("Undefined net_id: " + net_id); } return data; } public Object visit(ASTiterator_net_id node, Object data) { // foreach ($iterator_net_id in $@arr) { } // should never be called; used only to get the image return data; } public Object visit(ASTiterator_num_id node, Object data) { // foreach ($iterator_num_id in #@arr) { } // should never be called; used only to get the image return data; } public Object visit(ASTrrprod_definition node, Object data) { // Definition of a "production" in a right-recursive // phrase-structure grammar // Syntax: $>foo = a b c $>bar ; // parser returns AST tree (_not_ evaluated at this time) // rrprod_definition // rrprod_id: $>foo // rrProdRHS // regexp // // Need to allocate an RrKleeneVisitor and tell the AST regexp // daughter to accept it. This "right-recursive" visitor // 1) checks to make sure that any rrprod_id references are really // right-linear in the AST, and // 2) returns a HashSet of such "dependencies". // This method directly handles a rrprod_id on the LHS. // Do not eval the rrprod_id, but just get the Image directly // (When a rrprod_id on the RHS is _evaluated_, see ASTrrprod_id.) String rrprod_id = ((ASTrrprod_id) node.jjtGetChild(0)).getImage(); symmap.putsym(rrprod_id); // needs to be in the symbol table // Note that an rrprod_id gets a negative integer value // get the RHS AST (_not_ evaluated now) for storage in the // symbol table ASTrrProdRHS rhs = (ASTrrProdRHS) node.jjtGetChild(1); // Do NOT call .jjtAccept(this, data) here. // Just check the AST for well-formedness and save it in the // symbol table // (along with a HashSet of its dependencies). // Tell the RHS to accept a special visitor that checks to make // sure that the RHS contains only valid right-recursive references, // and returns a HashSet of them. // KRB: Could the same RrKleeneVisitor be re-used? RrKleeneVisitor rkv = new RrKleeneVisitor(); // The Boolean data value indicates whether an rrprod_id can appear // (in a right-recursive position); start with true. // The constructor of the // RrKleeneVisitor instantiates the HashSet returned; it // contains all the rr references like $>foo found on the // RHS (and the Visitor makes sure that any such references // are in legal places). HashSet<String> hs = (HashSet<String>) rhs.jjtAccept(rkv, new Boolean(true)); // save the AST, with the set of dependencies, in the symbol table for // later evaluation as part of a whole grammar of rr productions. // The value of the rrprod_id in the symbol table is an RrProdObject. env.put(rrprod_id, new RrProdObject(rhs, hs)); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(rrprod_id, SymtabIcons.RRPROD_IMAGE, data); } return data; } public Object visit(ASTrrprod_id node, Object data) { // This method is for rrprod_id ($>foo) on the RHS of a // right-linear production; // Compare to handling of multichar symbols String image = node.getImage(); int negcpv = symmap.putsym(image); // will be a NEGATIVE integer // A special temporary value; arcs labeled with negative // labels will be turned into eps:eps arcs, and repointed to // the start state of the target Fst, (via call to the $^start() // function; see rrGrammarConnect in kleeneopenfst.cc // create a two-state, one arc Fst with a // negative negcpv:negcpv label stack.push(lib.OneArcFst(negcpv)); return data; } public Object visit(ASTnum_assignment node, Object data) { // two children // num_id // numexp // Syntax: #num = 2 + 2 ; // See comments for ASTnet_assignment String num_id = ((ASTnum_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // internally in Kleene, all "ints" are stored as Long, // and all "floats" are stored as Double Object obj = stack.pop(); // a Long or Double object env.put(num_id, obj); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(num_id, SymtabIcons.NUM_IMAGE, data); PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); String value; if (obj instanceof Long) { value = "Long value: " + Long.toString((Long) obj); } else { value = "Double value: " + Double.toString((Double) obj); } terminal.appendToHistory("// " + value); } return data; } public Object visit(ASTnum_id node, Object data) { // called only when the num_id is on the RHS // need to look up and push the value from the environment String num_id = node.getImage(); Object obj = env.get(num_id); if (obj != null) { if (obj instanceof Long) { stack.push((Long) obj); } else if (obj instanceof Double) { stack.push((Double) obj); } return data; } else { throw new UndefinedIdException("Undefined num_id: " + num_id); } } public Object visit(ASTnet_func_assignment node, Object data) { // For a function "assignment", involving an equal sign, and // returning a net (Fst) value, e.g. // $^myfunc = $^($a, $b) { return $a | $b ; } // or // $^yourfunc = $^myfunc ; // creates an alias String func_id = ((ASTnet_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // should leave a FuncValue on the stack // Bind the func_id to the FuncValue in the environment env.put(func_id, stack.pop()); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(func_id, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnet_list_func_assignment node, Object data) { // For a function "assignment", involving an equal sign, and // returning a net list value, e.g. // $@^myfunc = $@^($a, $b) { return $a | $b ; } // or // $@^yourfunc = $@^myfunc ; // creates an alias String func_id = ((ASTnet_list_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // should leave a FuncValue on the stack // Bind the func_id to the FuncValue in the environment env.put(func_id, stack.pop()); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(func_id, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnum_list_func_assignment node, Object data) { // For a function "assignment", involving an equal sign, and // returning a num list value, e.g. // #@^myfunc = #@^($a, $b) { return $a | $b ; } // or // #@^yourfunc = #@^myfunc ; // creates an alias String func_id = ((ASTnum_list_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // should leave a FuncValue on the stack // Bind the func_id to the FuncValue in the environment env.put(func_id, stack.pop()); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(func_id, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTvoid_func_assignment node, Object data) { // For a function "assignment", involving an equal sign, and // returning nothing. String func_id = ((ASTvoid_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // should leave a FuncValue on the stack env.put(func_id, stack.pop()); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(func_id, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnet_func_definition node, Object data) { // For a net function "definition", e.g. // Syntax: $^myfunc($a, $b...) { } // three daughters in the AST // net_func_id // param_list (with 0,1 or 2 daughters) // required_params // optional_params (with default values indicated) // func_block String funcName = ((ASTnet_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // ASTparam_list // should leave an ArrayList<ParamSlot> on the stack ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // add to the environment (symtab) // A FuncValue always carries a handle to the current frame, i.e. the // frame in which the function was defined. References are searched // through the static links in the Environment, and this implements // lexical scope. env.put(funcName, new FuncValue(env.getCurrentFrame(), // the static frame pal, (ASTfunc_block) node.jjtGetChild(2))); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcName, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnet_list_func_definition node, Object data) { // For a net arr function "definition", e.g. // Syntax: $@^myfunc($a, $b...) { } // three daughters in the AST // net_list_func_id // param_list (with 0,1 or 2 daughters) // required_params // optional_params (with default values indicated) // func_block String funcName = ((ASTnet_list_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // ASTparam_list // should leave an ArrayList<ParamSlot> on the stack ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // add to the environment (symtab) // A FuncValue always carries a handle to the current frame, i.e. the // frame in which the function was defined. References are searched // through the static links in the Environment, and this implements // lexical scope. env.put(funcName, new FuncValue(env.getCurrentFrame(), // the static frame pal, (ASTfunc_block) node.jjtGetChild(2))); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcName, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnum_list_func_definition node, Object data) { // For a num arr function "definition", e.g. // Syntax: #@^myfunc(1, 2...) { } // three daughters in the AST // num_list_func_id // param_list (with 0,1 or 2 daughters) // required_params // optional_params (with default values indicated) // func_block String funcName = ((ASTnum_list_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // ASTparam_list // should leave an ArrayList<ParamSlot> on the stack ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // add to the environment (symtab) // A FuncValue always carries a handle to the current frame, i.e. the // frame in which the function was defined. References are searched // through the static links in the Environment, and this implements // lexical scope. env.put(funcName, new FuncValue(env.getCurrentFrame(), // the static frame pal, (ASTfunc_block) node.jjtGetChild(2))); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcName, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTvoid_func_definition node, Object data) { // for a void function "definition", e.g. // ^myfunc (#num) { info #num ; } // three daughters in the AST, now look like // void_func_id // param_list (with 0,1 or 2 daughters) // required_params // optional_params (with default values indicated) // func_block String funcName = ((ASTvoid_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // ASTparam_list // should leave an ArrayList<ParamSlot> on the stack ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // add to the environment (symtab) env.put(funcName, new FuncValue(env.getCurrentFrame(), // the static frame pal, (ASTfunc_block) node.jjtGetChild(2))); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcName, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnum_func_assignment node, Object data) { String func_id = ((ASTnum_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); env.put(func_id, stack.pop()); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(func_id, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnum_func_definition node, Object data) { // three daughters: num_func_id param_list func_block String funcName = ((ASTnum_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // Should leave an ArrayList on the stack ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); env.put(funcName, new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(2))); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcName, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnet_func_func_assignment node, Object data) { // See ASTnet_assignment for comments String funcFunc_id = ((ASTnet_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); env.put(funcFunc_id, stack.pop()); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcFunc_id, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTvoid_func_func_assignment node, Object data) { String funcFunc_id = ((ASTvoid_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // should leave a FuncValue object on the stack env.put(funcFunc_id, stack.pop()); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcFunc_id, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnet_func_func_definition node, Object data) { // 3 daughters String funcFuncName = ((ASTnet_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); env.put(funcFuncName, new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(2))); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcFuncName, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTvoid_func_func_definition node, Object data) { // 3 daughters String funcFuncName = ((ASTvoid_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); env.put(funcFuncName, new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(2))); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcFuncName, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnet_func_func_id node, Object data) { // This method is called for net_func_func_id on the right-hand-side, // so it needs to successfully look up the value (or throw // an exception); for an assignment, e.g. $^^func() = // this visit method is not called for the LHS id String net_func_func_id = node.getImage(); Object obj = env.get(net_func_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined net_func_func_id: " + net_func_func_id); } return data; } public Object visit(ASTnet_list_func_func_id node, Object data) { // This method is called for net_list_func_func_id on the right-hand-side, // so it needs to successfully look up the value (or throw // an exception); for an assignment, e.g. $@^^func() = // this visit method is not called for the LHS id String net_list_func_func_id = node.getImage(); Object obj = env.get(net_list_func_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined net_list_func_func_id: " + net_list_func_func_id); } return data; } public Object visit(ASTvoid_func_func_id node, Object data) { // this is called for void_func_func_id on the right-hand-side, // so it needs to successfully look up the value (or throw // an exception); for an assignment, e.g. ^^func() = // this visit method is not called for the LHS id String void_func_func_id = node.getImage(); Object obj = env.get(void_func_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined void_func_func_id: " + void_func_func_id); } return data; } public Object visit(ASTnum_func_func_assignment node, Object data) { String funcFunc_id = ((ASTnum_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // should leave a FuncValue object on the stack env.put(funcFunc_id, stack.pop()); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcFunc_id, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnum_func_func_definition node, Object data) { // three daughters: num_func_func_id param_list func_block String funcFuncName = ((ASTnum_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); env.put(funcFuncName, new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(2))); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { addToGUISymtab(funcFuncName, SymtabIcons.FUNC_IMAGE, data); } return data; } public Object visit(ASTnum_func_func_id node, Object data) { // this method is called for a num_func_func_id on the RHS; // need to look up and push the value from the environment String num_func_func_id = node.getImage(); Object obj = env.get(num_func_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined num_func_func_id: " + num_func_func_id); } return data; } public Object visit(ASTnet_list_assignment node, Object data) { String net_list_id = ((ASTnet_list_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // child 1 could be net_list_id, net_list_lit or net_list_func_call // Should leave a NetList object on the stack (see Fst.java) NetList netList = (NetList) (stack.pop()); // relate the net_list_id and the NetList in the current Frame env.put(net_list_id, netList); // Now if the current Frame is the main Frame, add // an appropriate SymbolIconButton to the window. In other // words, do NOT try to add a SymbolIconButton if the // current Frame is a temporary one used to process a // function call, or a stand_alone_block, // and do NOT try to add a SymbolIconButton // if the interpreter is working on a startup script (i.e. // is not operating within the interactive GUI) if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { // then we're in the GUI // add an Icon to the Symtab window addToGUISymtab(net_list_id, SymtabIcons.NET_ARR_IMAGE, data); PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); terminal.appendToHistory("// " + basicNetListInfo(netList)); } return data; } public Object visit(ASTnum_list_assignment node, Object data) { String num_list_id = ((ASTnum_list_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); // child 1 could be num_list_id, num_list_lit or num_list_func_call // Should leave a NumList object on the stack (see Fst.java) NumList numList = (NumList) (stack.pop()); // relate the num_list_id and the NumList in the current Frame env.put(num_list_id, numList); // Now if the current Frame is the main Frame, add // an appropriate SymbolIconButton to the window. In other // words, do NOT try to add a SymbolIconButton if the // current Frame is a temporary one used to process a // function call, or a stand_alone_block, // and do NOT try to add a SymbolIconButton // if the interpreter is working on a startup script (i.e. // is not operating within the interactive GUI) if (((InterpData) data).getInGUI() == true && env.getCurrentFrame() == mainFrame) { // then we're in the GUI // add an Icon to the Symtab window addToGUISymtab(num_list_id, SymtabIcons.NUM_ARR_IMAGE, data); PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); terminal.appendToHistory("// Number list value: Size " + numList.size()); } return data; } public Object visit(ASTif_statement node, Object data) { // daughters are if_part elsif_part* else_part? int childCount = node.jjtGetNumChildren(); for (int i = 0; i < childCount; i++) { node.jjtGetChild(i).jjtAccept(this, data); // break loop for the first daughter that succeeds (returns true) if (lib.isTrue(stack.pop())) { break; } } return data; } public Object visit(ASTboolean_test node, Object data) { // just an arithmetic expression node.childrenAccept(this, data); // leaves a Long or Double value on the stack return data; } public Object visit(ASTif_part node, Object data) { // two daughters: boolean_test block (or other stmt) node.jjtGetChild(0).jjtAccept(this, data); if (lib.isTrue(stack.pop())) { node.jjtGetChild(1).jjtAccept(this, data); // "return" true to block evaluation of elsif or else blocks stack.push(new Long(1L)); } else { // "return" false stack.push(new Long(0L)); } return data; } public Object visit(ASTelsif_part node, Object data) { // two daughters: boolean_test block (or other stmt) node.jjtGetChild(0).jjtAccept(this, data); if (lib.isTrue(stack.pop())) { node.jjtGetChild(1).jjtAccept(this, data); stack.push(new Long(1L)); } else { stack.push(new Long(0L)); } return data; } public Object visit(ASTelse_part node, Object data) { // one daughter: block (or other stmt) node.jjtGetChild(0).jjtAccept(this, data); // Yes, return "true" here. ASTif_statement always pops off // the value "returned" stack.push(new Long(1L)); return data; } public Object visit(ASTforeach_net_iteration_statement node, Object data) { // foreach ($foo in $@arr) { } // three daughters // iterator_net_id() // net_list_exp() // loop_block || statement String net_id = ((ASTiterator_net_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); Object obj = node.jjtGetChild(2); // an AST // do not allocate a new frame (cf. Python) if (obj instanceof ASTloop_block) { ASTloop_block loop_block = (ASTloop_block) obj; for (Iterator<Fst> i = netList.iterator(); i.hasNext();) { // bind the net_id in the new Frame env.put(net_id, i.next()); // execute the block loop_block.jjtAccept(this, data); if (((InterpData) data).getLoopContinue()) { ((InterpData) data).setLoopContinue(false); continue; } else if (((InterpData) data).getLoopBreak()) { ((InterpData) data).setLoopBreak(false); break; } else if (((InterpData) data).getFuncReturn()) { // a 'return' statement // don't reset data here break; } else if (((InterpData) data).getQuitSession()) { // a 'quit' statement // don't reset data here break; } } } else { for (Iterator<Fst> i = netList.iterator(); i.hasNext();) { env.put(net_id, i.next()); // execute the statement node.jjtGetChild(2).jjtAccept(this, data); } } return data; } public Object visit(ASTforeach_num_iteration_statement node, Object data) { // foreach (#foo in #@arr) { } // three daughters // iterator_num_id() // num_list_exp() // loop_block || statement String num_id = ((ASTiterator_num_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); Object obj = node.jjtGetChild(2); // an AST if (obj instanceof ASTloop_block) { ASTloop_block loop_block = (ASTloop_block) obj; for (Iterator i = numList.iterator(); i.hasNext();) { // bind the net_id in the new Frame env.put(num_id, i.next()); // execute the block loop_block.jjtAccept(this, data); if (((InterpData) data).getLoopContinue()) { ((InterpData) data).setLoopContinue(false); continue; } else if (((InterpData) data).getLoopBreak()) { ((InterpData) data).setLoopBreak(false); break; } else if (((InterpData) data).getFuncReturn()) { // a 'return' statement // don't reset data here break; } else if (((InterpData) data).getQuitSession()) { // a 'quit' statement // don't reset data here break; } } } else { for (Iterator i = numList.iterator(); i.hasNext();) { env.put(num_id, i.next()); // execute the statement node.jjtGetChild(2).jjtAccept(this, data); } } return data; } public Object visit(ASTwhile_statement node, Object data) { // two daughters: boolean_test and ( loop_block || statement ) ASTboolean_test boolean_test = (ASTboolean_test) node.jjtGetChild(0); Object obj = node.jjtGetChild(1); if (obj instanceof ASTloop_block) { while (true) { // re-evaluate the boolean_test (an AST) each time; // should leave a Long or a Double on the stack boolean_test.jjtAccept(this, data); if (lib.isTrue(stack.pop())) { // then eval the block (an AST) node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getLoopContinue()) { ((InterpData) data).setLoopContinue(false); continue; } else if (((InterpData) data).getLoopBreak()) { ((InterpData) data).setLoopBreak(false); break; } else if (((InterpData) data).getFuncReturn()) { // a 'return' statement // don't reset data here break; } else if (((InterpData) data).getQuitSession()) { // a 'quit' statement // don't reset data here break; } } else { // end of the while loop break; } } } else { while (true) { boolean_test.jjtAccept(this, data); if (lib.isTrue(stack.pop())) { //statement.jjtAccept(this, data) ; node.jjtGetChild(1).jjtAccept(this, data); } } } return data; } public Object visit(ASTuntil_statement node, Object data) { // two daughters: boolean_test and ( loop_block | statement ) ASTboolean_test boolean_test = (ASTboolean_test) node.jjtGetChild(0); Object obj = node.jjtGetChild(1); if (obj instanceof ASTloop_block) { while (true) { // re-evaluate the boolean_test AST each time; // should leave a Long or a Double on the stack boolean_test.jjtAccept(this, data); // N.B. the logical not (!) here, compared to while_statement above if (!lib.isTrue(stack.pop())) { node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getLoopContinue()) { ((InterpData) data).setLoopContinue(false); continue; } else if (((InterpData) data).getLoopBreak()) { ((InterpData) data).setLoopBreak(false); break; } else if (((InterpData) data).getFuncReturn()) { // don't reset data here break; } else if (((InterpData) data).getQuitSession()) { // don't reset data here break; } } else { break; } } } else { while (true) { boolean_test.jjtAccept(this, data); if (!lib.isTrue(stack.pop())) { node.jjtGetChild(1).jjtAccept(this, data); } } } return data; } public Object visit(ASTreturn_statement node, Object data) { // in a void function, will have no daughters // else it will have exactly one daughter; this is syntactically // constrained, either 0 or 1 daughter int childCount = node.jjtGetNumChildren(); if (childCount != 0) { node.jjtGetChild(0).jjtAccept(this, data); // should leave an object on the stack } // if childCount == 0, then it was a bare return ; stmt // should be in a void function ((InterpData) data).setFuncReturn(true); // this causes the function block to terminate return data; } public Object visit(ASTnumexp node, Object data) { // should be just one child node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double on the stack return data; } public Object visit(ASTboolean_or_exp node, Object data) { // should be two daughters // do a short-stop, left-to-right evaluation node.jjtGetChild(0).jjtAccept(this, data); if (lib.isTrue(stack.pop())) { // no need to evaluate the second child stack.push(new Long(1L)); } else { // evaluate the second daughter node.jjtGetChild(1).jjtAccept(this, data); if (lib.isTrue(stack.pop())) stack.push(new Long(1L)); else stack.push(new Long(0L)); } return data; } public Object visit(ASTboolean_and_exp node, Object data) { // should be two daughters // do a short-stop, left-to-right evaluation node.jjtGetChild(0).jjtAccept(this, data); if (lib.isTrue(stack.pop())) { // So far so good. // now evaluate the second daughter node.jjtGetChild(1).jjtAccept(this, data); if (lib.isTrue(stack.pop())) // then both are true stack.push(new Long(1L)); // leave "true" on the stack else stack.push(new Long(0L)); // leave "false" on the stack } else // the first daughter was false, so the whole boolean_and_exp is false stack.push(new Long(0L)); // leave "false" on the stack return data; } public Object visit(ASTboolean_not_exp node, Object data) { // should be only one daughter, syntactically constrained node.jjtGetChild(0).jjtAccept(this, data); // the _not_ is handled here if (lib.isTrue(stack.pop())) stack.push(new Long(0L)); // then return False else stack.push(new Long(1L)); // else return True return data; } public Object visit(ASTless_than_exp node, Object data) { // two daughters, either Long or Double node.childrenAccept(this, data); Object second = stack.pop(); Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) if (((Long) first).longValue() < ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Long) first).longValue() < ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } else { if (second instanceof Long) if (((Double) first).doubleValue() < ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Double) first).doubleValue() < ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } return data; } public Object visit(ASTless_than_or_equal_exp node, Object data) { // two daughters, either Long or Double node.childrenAccept(this, data); Object second = stack.pop(); Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) if (((Long) first).longValue() <= ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Long) first).longValue() <= ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } else { if (second instanceof Long) if (((Double) first).doubleValue() <= ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Double) first).doubleValue() <= ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } return data; } public Object visit(ASTgreater_than_exp node, Object data) { // two daughters, either Long or Double node.childrenAccept(this, data); Object second = stack.pop(); Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) if (((Long) first).longValue() > ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Long) first).longValue() > ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } else { if (second instanceof Long) if (((Double) first).doubleValue() > ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Double) first).doubleValue() > ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } return data; } public Object visit(ASTgreater_or_equal_exp node, Object data) { // two daughters, either Long or Double node.childrenAccept(this, data); Object second = stack.pop(); Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) if (((Long) first).longValue() >= ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Long) first).longValue() >= ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } else { if (second instanceof Long) if (((Double) first).doubleValue() >= ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Double) first).doubleValue() >= ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } return data; } public Object visit(ASTequal_exp node, Object data) { // two daughters, either Long or Double node.childrenAccept(this, data); Object second = stack.pop(); Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) if (((Long) first).longValue() == ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Long) first).longValue() == ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } else { if (second instanceof Long) if (((Double) first).doubleValue() == ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Double) first).doubleValue() == ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } return data; } public Object visit(ASTnot_equal_exp node, Object data) { // two daughters, either Long or Double node.childrenAccept(this, data); Object second = stack.pop(); Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) if (((Long) first).longValue() != ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Long) first).longValue() != ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } else { if (second instanceof Long) if (((Double) first).doubleValue() != ((Long) second).longValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); else if (((Double) first).doubleValue() != ((Double) second).doubleValue()) stack.push(new Long(1L)); else stack.push(new Long(0L)); } return data; } public Object visit(ASTaddition_exp node, Object data) { // always two daughters (Long or Double), constrained syntactically node.childrenAccept(this, data); Object second = stack.pop(); // N.B. pop off the 2nd arg first Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) { // both args are Long stack.push(new Long(((Long) first).longValue() + ((Long) second).longValue())); } else { // one arg Long, one Double, promoted to Double stack.push(new Double(((Long) first).longValue() + ((Double) second).doubleValue())); } } else { // first is Double if (second instanceof Double) { // both args are Double stack.push(new Double(((Double) first).doubleValue() + ((Double) second).doubleValue())); } else { // one arg Double, one Long, promotes to Double stack.push(new Double(((Double) first).doubleValue() + ((Long) second).longValue())); } } return data; } public Object visit(ASTsubtraction_exp node, Object data) { // N.B. "subtraction" means arithmetic subtraction in // Kleene; // See "difference" for subtraction of Fsts // always two daughters (Long or Double), syntactically constrained node.childrenAccept(this, data); Object second = stack.pop(); // N.B. pop off the 2nd arg first Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) { // both args are Long stack.push(new Long(((Long) first).longValue() - ((Long) second).longValue())); } else { // first arg Long, second Double, promotes to Double stack.push(new Double(((Long) first).longValue() - ((Double) second).doubleValue())); } } else { // first is Double if (second instanceof Double) { // both args are Double stack.push(new Double(((Double) first).doubleValue() - ((Double) second).doubleValue())); } else { // first arg Double, second Long, promotes to Double stack.push(new Double(((Double) first).doubleValue() - ((Long) second).longValue())); } } return data; } public Object visit(ASTmult_exp node, Object data) { // always two daughters (Long or Double), syntactically constrained node.childrenAccept(this, data); Object second = stack.pop(); // N.B. pop off the 2nd arg first Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) { // both args are Long stack.push(new Long(((Long) first).longValue() * ((Long) second).longValue())); } else { // first arg Long, second Double, promotes to Double stack.push(new Double(((Long) first).longValue() * ((Double) second).doubleValue())); } } else { // first is Double if (second instanceof Double) { // both args are Double stack.push(new Double(((Double) first).doubleValue() * ((Double) second).doubleValue())); } else { // first arg Double, second Long, promotes to Double stack.push(new Double(((Double) first).doubleValue() * ((Long) second).longValue())); } } return data; } public Object visit(ASTdiv_exp node, Object data) { // always two daughters (Long or Double) node.childrenAccept(this, data); Object second = stack.pop(); // N.B. pop off the 2nd arg first Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) { // both args are Long stack.push(new Long(((Long) first).longValue() / ((Long) second).longValue())); } else { // first arg Long, second Double, promotes to Double stack.push(new Double(((Long) first).longValue() / ((Double) second).doubleValue())); } } else { // first is Double if (second instanceof Double) { // both args are Double stack.push(new Double(((Double) first).doubleValue() / ((Double) second).doubleValue())); } else { // first arg Double, second Long, promotes to Double stack.push(new Double(((Double) first).doubleValue() / ((Long) second).longValue())); } } return data; } public Object visit(ASTmod_exp node, Object data) { // always two daughters (Long or Double) node.childrenAccept(this, data); Object second = stack.pop(); // N.B. pop off the 2nd arg first Object first = stack.pop(); if (first instanceof Long) { if (second instanceof Long) { // both args are Long stack.push(new Long(((Long) first).longValue() % ((Long) second).longValue())); } else { // first arg Long, second Double, promotes to Double stack.push(new Double(((Long) first).longValue() % ((Double) second).doubleValue())); } } else { // first is Double if (second instanceof Double) { // both args are Double stack.push(new Double(((Double) first).doubleValue() % ((Double) second).doubleValue())); } else { // first arg Double, second Long, promotes to Double stack.push(new Double(((Double) first).doubleValue() % ((Long) second).longValue())); } } return data; } public Object visit(ASTunary_minus_exp node, Object data) { node.childrenAccept(this, data); Object obj = stack.pop(); if (obj instanceof Long) { stack.push(new Long(((Long) obj).longValue() * -1)); } else { stack.push(new Double(((Double) obj).doubleValue() * -1.0)); } return data; } public Object visit(ASTnum_func_call node, Object data) { // E.g. #^add(1,2) or // #^(...){...}(1,2) // two daughters: num_func_exp arg_list // the num_func_exp could be num_func_id or an anon function, // so need to evaluate it to get the value node.jjtGetChild(0).jjtAccept(this, data); // It should leave a FuncValue object on the stack FuncValue funcValue = (FuncValue) stack.pop(); // the second daughter is an argument list node.jjtGetChild(1).jjtAccept(this, data); // the top object left on the stack is a count of the arguments ArgCounts ac = (ArgCounts) stack.pop(); // Now allocate a new Frame for the execution of the function call // The FuncValue object stores a handle to the "static" Frame // in which it was originally defined. (Free variables are // looked up starting at this static Frame, implementing // Lexical Scope.) env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the args try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch any Exceptions and release the Frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block) try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the Frame created for the execution of the func call env.releaseFrame(); // Check the return value; there should be a Long or Double left on // the stack. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("Num valued function call fails to return a Long or Double."); } else if (!((obj instanceof Long) | (obj instanceof Double))) { throw new FuncCallException("Number-valued function call returns incorrect type."); } return data; } public Object visit(ASTnum_func_exp node, Object data) { // could be a num_func_id or a num_func_anon_exp node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTnum_func_id node, Object data) { // this method is called for a num_func_id on the RHS; // need to look up and push the value from the environment String num_func_id = node.getImage(); Object obj = env.get(num_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined num_func_id: " + num_func_id); } return data; } public Object visit(ASTnum_func_anon_exp node, Object data) { // two daughters: param_list func_block node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } public Object visit(ASTnum_func_func_call node, Object data) { // E.g. #^^increment_by(1) or // #^^(...){...}(1) // two daughters: num_func_func_exp arg_list // the num_func_func_exp could be num_func_func_id or an // anon function, // so need to evaluate it to get the value node.jjtGetChild(0).jjtAccept(this, data); // should leave a FuncValue on the stack FuncValue funcValue = (FuncValue) stack.pop(); node.jjtGetChild(1).jjtAccept(this, data); // the top object left on the stack is a count of the arguments ArgCounts ac = (ArgCounts) stack.pop(); // Now allocate a new Frame for the execution of the function call // The FuncValue object stores a handle to the "static" Frame // in which it was originally defined. (Free variables are // looked up starting at this static Frame, implementing // Lexical Scope.) env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the args try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all exceptions and release the frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block) try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the Frame created for the execution of the func call env.releaseFrame(); // Check the return value; there should be a FuncValue left on // the stack. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("NumFuncFunc valued function call fails to return a value."); } else if (!(obj instanceof FuncValue)) { throw new FuncCallException("NumFuncFunc valued function call returns incorrect type."); } return data; } public Object visit(ASTnum_list_func_func_call node, Object data) { // E.g. #@^^name(1) or // #@^^(...){...}(1) // two daughters: num_list_func_func_exp arg_list // the num_list_func_func_exp could be num_list_func_func_id or // an anonymous function, // so need to evaluate it to get the function value node.jjtGetChild(0).jjtAccept(this, data); // should leave a FuncValue on the stack FuncValue funcValue = (FuncValue) stack.pop(); node.jjtGetChild(1).jjtAccept(this, data); // the top object left on the stack is a count of the arguments ArgCounts ac = (ArgCounts) stack.pop(); // Now allocate a new Frame for the execution of the function call // The FuncValue object stores a handle to the "static" Frame // in which it was originally defined. (Free variables are // looked up starting at this static Frame, implementing // Lexical Scope.) env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the args try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all exceptions and release the frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block) try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the Frame created for the execution of the func call env.releaseFrame(); // Check the return value; there should be a FuncValue left on // the stack. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("NumListFuncFunc valued function call fails to return a value."); } else if (!(obj instanceof FuncValue)) { throw new FuncCallException("NumListFuncFunc valued function call returns incorrect type."); } return data; } public Object visit(ASTnet_list_func_func_call node, Object data) { // E.g. $@^^name(1) or // $@^^(...){...}(1) // two daughters: net_list_func_func_exp arg_list // the net_list_func_func_exp could be net_list_func_func_id or // an anonymous function, // so need to evaluate it to get the function value node.jjtGetChild(0).jjtAccept(this, data); // should leave a FuncValue on the stack FuncValue funcValue = (FuncValue) stack.pop(); node.jjtGetChild(1).jjtAccept(this, data); // the top object left on the stack is a count of the arguments ArgCounts ac = (ArgCounts) stack.pop(); // Now allocate a new Frame for the execution of the function call // The FuncValue object stores a handle to the "static" Frame // in which it was originally defined. (Free variables are // looked up starting at this static Frame, implementing // Lexical Scope.) env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the args try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all exceptions and release the frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block) try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the Frame created for the execution of the func call env.releaseFrame(); // Check the return value; there should be a FuncValue left on // the stack. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("NetListFuncFunc valued function call fails to return a value."); } else if (!(obj instanceof FuncValue)) { throw new FuncCallException("NetListFuncFunc valued function call returns incorrect type."); } return data; } public Object visit(ASTnum_func_func_exp node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTnum_list_func_func_exp node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTnet_list_func_func_exp node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTnum_func_func_anon_exp node, Object data) { // two daughters: param_list func_block node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } public Object visit(ASTdec_int_literal node, Object data) { stack.push(new Long(node.getLongValue())); return data; } public Object visit(ASThex_int_literal node, Object data) { stack.push(new Long(node.getLongValue())); return data; } public Object visit(ASTdec_float_literal node, Object data) { stack.push(new Double(node.getDoubleValue())); return data; } public Object visit(ASTrrProdRHS node, Object data) { // always has a single daughter: ASTregexp node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTregexp node, Object data) { // There should be just one daughter node.jjtGetChild(0).jjtAccept(this, data); // and should leave an Fst node on the stack Fst fst = (Fst) stack.peek(); correctSigmaOther(fst); return data; } public Object visit(ASTcomposed_exp node, Object data) { // cf. to method for intersected_exp // A _o_ B _o_ C ... is parsed into a flat AST // (treated like an n-ary operation) // there will always be at least two daughters // (this is syntactically constrained) // Beware memory leaks (because the OpenFst Compose(A,B) is // non-destructive, creating a new OpenFst (C++) object) // start by getting a new sigma* Java Fst (hasOther will be // set to true); all the daughter Fsts will be composed // onto it (on the "bottom") one at a time Fst resultFst = lib.UniversalLanguageFst(); int daughterCount = node.jjtGetNumChildren(); // Loop through the daughters, composing each one with // ("on the bottom of") the resultFst for (int i = 0; i < daughterCount; i++) { node.jjtGetChild(i).jjtAccept(this, data); // leaves an Fst object on the stack Fst daughterFst = (Fst) stack.pop(); resultFst = lib.Compose(resultFst, daughterFst); } stack.push(resultFst); return data; } public Object visit(ASTrule_lhs node, Object data) { // alternation rules always have a LHS, // and usually an ASTrule_lhs // with exactly two children: // 0. ASTrule_lhs_upper or ASTrule_lhs_markup // 1. ASTrule_lhs_lower or ASTrule_lhs_markup // (The exception is a "transducer" rule, which has // ASTrule_lhs_transducer (parallel to ASTrule_lhs)) // create a new RuleSyntacticParts to hold the LHS ASTs // (not to be interpreted yet) DON'T eval them here. RuleSyntacticParts rsp = null; // If one of the daughters is an ASTrule_lhs_markup node, then we have // a Markup Rule Object objUpper = node.jjtGetChild(0); Object objLower = node.jjtGetChild(1); if (objUpper instanceof ASTrule_lhs_upper) { // objLower could be either ASTrule_lhs_lower or ASTrule_lhs_markup if (objLower instanceof ASTrule_lhs_lower) { // straightforward mapping rule upper -> lower rsp = new RuleSyntacticParts((ASTrule_lhs_upper) objUpper, (ASTrule_lhs_lower) objLower); } else { // right-arrow markup rule: X -> Y ~~~ Z // or X -> Y ~~~ (lacking a right insertion) // or X -> ~~~ Z (lacking a left insertion) // or X -> ~~~ (no insertions, a no-op) ((ASTrule_lhs_markup) objLower).jjtAccept(this, data); // this leaves a MarkupParts object on the stack MarkupParts markupParts = (MarkupParts) stack.pop(); rsp = new RuleSyntacticParts((ASTrule_lhs_upper) objUpper, markupParts.getLeftMarkupInsertion(), markupParts.getRightMarkupInsertion()); } } else { // the objUpper is of type ASTrule_lhs_markup // the objLower is of type ASTrule_lhs_lower // // left-arrow markup rule // Y ~~~ Z <- X // Y ~~~ <- X (lacking a right insertion) // ~~~ Z <- X (lacking a left insertion) // ~~~ <- X (no insertions, a no-op) ((ASTrule_lhs_markup) objUpper).jjtAccept(this, data); // this leaves a MarkupParts object on the stack MarkupParts markupParts = (MarkupParts) stack.pop(); rsp = new RuleSyntacticParts(markupParts.getLeftMarkupInsertion(), markupParts.getRightMarkupInsertion(), (ASTrule_lhs_lower) objLower); } stack.push(rsp); return data; } public Object visit(ASTrule_lhs_transducer node, Object data) { // rule LHS written as a transducer, e.g. the a:b in // a:b -> / left _ right or the a*:b in a*:b -> / l _ r // these transducer rules cannot be Markup rules // create a new RuleSyntacticParts to hold the LHS AST // (not to be interpreted yet) DON'T eval it here. // just one child, some kind of regexp AST stack.push(new RuleSyntacticParts(node)); return data; } public Object visit(ASTrule_lhs_upper node, Object data) { // should be one daughter, some kind of regexp node.jjtGetChild(0).jjtAccept(this, data); // leave the Fst on the stack return data; } public Object visit(ASTrule_lhs_lower node, Object data) { // should be one daughter, some kind of regexp node.jjtGetChild(0).jjtAccept(this, data); // leave the Fst on the stack return data; } public Object visit(ASTrule_lhs_markup node, Object data) { // daughters: ASTleft_markup_insertion and ASTright_markup_insertion, // both optional // A MarkupParts objects holds handles to the ASTs, which can be null MarkupParts markupParts = new MarkupParts(); for (int i = 0; i < node.jjtGetNumChildren(); i++) { Object obj = node.jjtGetChild(i); if (obj instanceof ASTleft_markup_insertion) { markupParts.setLeftMarkupInsertion((ASTleft_markup_insertion) obj); } else if (obj instanceof ASTright_markup_insertion) { markupParts.setRightMarkupInsertion((ASTright_markup_insertion) obj); } } stack.push(markupParts); return data; } public Object visit(ASTleft_markup_insertion node, Object data) { // should be one daughter, some kind of regexp node.jjtGetChild(0).jjtAccept(this, data); // leave the Fst on the stack return data; } public Object visit(ASTright_markup_insertion node, Object data) { // should be one daughter, some kind of regexp node.jjtGetChild(0).jjtAccept(this, data); // leave the Fst on the stack return data; } Fst compileRuleSemanticParts(ArrayList<RuleSemanticParts> listOfSemanticParts) { // KRB: debug //System.out.println("\nEntering compileRuleSemanticParts()\n") ; //System.out.println("\nNumber of RuleSemanticParts: " + listOfSemanticParts.size()) ; // The listOfSemanticParts contains potentially multiple RuleSemanticParts // objects, which are to be compiled in parallel. The information in the // possible multiple RuleSemanticParts is used to define three finite-state // machines that are eventually intersected: Base & Context & Constraints // to make the final Fst result representing all the RuleSemanticParts. // Initialize some structures for collecting Base, Context and Constraints // (or parts thereof) for _all_ the rules being compiled in parallel. // Parallelism can result from: // 1. Overt calls to $^parallel( rule, rule, ...) // 2. Where clauses: // $a -> $b / _ # { where $a _E_ $@(b, d, g), $b _E_ $@(p, t, k) } // which is an abbreviation for 3 (parallel) rules // ( b -> p / _ # , d -> t / _ #, g -> k / _ # ) // 3. Epenthesis in rules: // a* -> b gets interpreted as two parallel rules // ( (a* - "") -> b, "" -> b ) // In the definition of Base there is one CP(A,B), i.e. hulden.CP(upper, lower), // or CPMarkup...(X, Y, Z) // representing the LHS, for each semantic rule; the CPs for all the parallel // rules are _unioned_ together in the computation of Base, so // start with an empty-language fst and union all the CPs into it. Fst BaseUnionOfCPs = lib.EmptyLanguageFst(); // Rule compilation involves the calculation of a language restriction, // which may have multiple context parts (one for each individual context // in all the parallel rules). To accomplish this for a set of parallel // rules, each context L _ R is interpreted as [L x \x* x R], where x is // a special restriction delimiter used only internally. These networks, // one for each context, get unioned together, and then at the end // the resulting unionOfContexts is surrounded with \x*, i.e. // [\x* unionOfContexts \x*] // Start with a unionOfContexts that is the empty language and union all // the [L x \x* x R] parts into it. The surrounding \x* and \x* will be // added later. Fst unionOfContexts = lib.EmptyLanguageFst(); // The Constraints, potentially one for each context in all the parallel rules // (there can be more than one context per rule) are _intersected_ together. // Start with .*, the Universal Language and intersect constraints into it. // Constraints are used only for obligatory rules. (Optionality in a rule // corresponds to a lack of such constraints for that particular rule.) Fst Constraints = lib.UniversalLanguageFst(); // iterate through the list of RuleSemanticParts objects that need to be // compiled in parallel for (Iterator<RuleSemanticParts> iter = listOfSemanticParts.iterator(); iter.hasNext();) { // KRB: debug //System.out.println("Loop start for one RuleSemanticPart") ; RuleSemanticParts rsempt = iter.next(); // Uppercase letters A and B used here for variables to facilitate // comparison with Hulden's rule-compilation examples. // Symbols A and B are used herein for straightforward rewrite rules // (also needed for constraints) Fst A = null; // upper LHS Fst B = null; // lower LHS Fst ABTransducer = null; // used only for transducer rules // Symbols X, Y and Z are used only for markup rules (X is the "input" // expression, which could be upper or lower // depending on the rule arrow direction; Y is always the left insertion, // Z is always the right insertion) either // X -> Y ... Z // or // Y ... Z <- X // Used for Markup Rules Fst X = null; // input side (upper or lower) Fst Y = null; // leftMarkupInsertion Fst Z = null; // rightMarkupInsertion RuleArrowType arrowType = rsempt.getArrowType(); // RuleArrowType.RIGHT or .LEFT RuleObligType obligType = rsempt.getObligType(); // RuleObligType.OBLIG or .OPT RuleMatchType matchType = rsempt.getMatchType(); // RuleMatchType.ALL, .MAX_L2R, .MIN_L2R, .MAX_R2L, .MIN_R2L RuleMapType mapType = rsempt.getMapType(); // RuleMapType.MAP or .MARKUP boolean epenthesis = rsempt.getEpenthesis(); // true iff epenthesis if (rsempt.getTransducerLhs() != null) { // KRB: debug //System.out.println("This is a transducer-style rule, point A.") ; // Then this is a transducer rule, e.g. a:b -> / ... // transducer rules cannot be markup rules ABTransducer = rsempt.getTransducerLhs(); ABTransducer.setFromSymtab(true); // protect it when extracting projections // KRB: debug //System.out.println("Dumping ABTransducer at point A") ; //lib.FstDump(ABTransducer) ; // Separate the LHS, currently a transducer, into separate input and output // projections // The algorithm needs one of these (A or B, depending on the arrow direction) // for computing Constraints, but the cp (crossproduct) is computed directly // from the ABTransducer A = hulden.CleanupSpecialSymbolsAction(lib.InputProjection(ABTransducer)); A.setFromSymtab(true); B = hulden.CleanupSpecialSymbolsAction(lib.OutputProjection(ABTransducer)); B.setFromSymtab(true); } else if (mapType == RuleMapType.MARKUP) { // markup rules like a -> IL ... IR / ... // (transducer rules cannot be markup rules) // // Set X, Y and Z here // set X (input, upper or lower), Y (left insertion) and Z (right insertion) // Note that CleanupSpecialSymbolsAction was called on all these parts when // the RuleSemanticParts was created if (arrowType == RuleArrowType.RIGHT) { // the input is the upper left-hand side X = rsempt.getUpperLhs(); } else { // the input is the lower left-hand side X = rsempt.getLowerLhs(); } X.setFromSymtab(true); // if Y and/or Z were missing (null) in the syntax, they were // previously assigned to EmptyStringLanguageFst Y = rsempt.getLeftMarkupInsertion(); Y.setFromSymtab(true); Z = rsempt.getRightMarkupInsertion(); Z.setFromSymtab(true); } else { // straightforward mapping rule like A -> B // Note that CleanupSpecialSymbolsAction was called on both these // parts when the RuleSemanticParts was created // // Set A and B here (two acceptors) // straightforward MAP rule A = rsempt.getUpperLhs(); A.setFromSymtab(true); B = rsempt.getLowerLhs(); B.setFromSymtab(true); } // cp for the Hulden "cross-product" of the LHS expressions Fst cp = null; if (ABTransducer != null) { // then it's a transducer rule; // just "flatten" the transducer specified in the syntax cp = hulden.CPflatten(ABTransducer); // KRB: debug //System.out.println("dumping the ASTransducer at point B") ; //lib.FstDump(ABTransducer) ; //System.out.println("dumping the flattened cp transducer at point Bprime") ; //lib.FstDump(cp) ; } else if (mapType == RuleMapType.MAP) { // It's a straightforward mapping rule, compute hulden.CP() cp = hulden.CP(A, B); } else { // It's a Markup Rule--use a different CPMarkup if (arrowType == RuleArrowType.RIGHT) { // right-arrow Markup Rule: X -> Y ... Z cp = hulden.CPMarkupRightArrow(X, Y, Z); } else { // left-arrow Markup Rule: Y ... Z <- X cp = hulden.CPMarkupLeftArrow(X, Y, Z); } } cp.setFromSymtab(true); // union this cp (one for each semantic rule) into the BaseUnionOfCPs // UnionIntoFirstInPlace() is destructive of the first argument, as desired here. BaseUnionOfCPs = lib.UnionIntoFirstInPlace(BaseUnionOfCPs, cp); // Now get the contexts of the current rule ArrayList<RuleContextSemanticParts> contexts = rsempt.getContexts(); // a rule written without a context will have the universal context // .* _ .* // assigned automatically // iterate through the contexts for the current SemanticRuleParts for (Iterator<RuleContextSemanticParts> rciter = contexts.iterator(); rciter.hasNext();) { RuleContextSemanticParts rcsp = rciter.next(); // CleanupSpecialSymbolsContext was called on the context // parts when the RuleContextSemanticParts was created Fst leftUpperContext = rcsp.getLeftUpperContext(); if (leftUpperContext != null) leftUpperContext.setFromSymtab(true); Fst leftLowerContext = rcsp.getLeftLowerContext(); if (leftLowerContext != null) leftLowerContext.setFromSymtab(true); Fst rightUpperContext = rcsp.getRightUpperContext(); if (rightUpperContext != null) rightUpperContext.setFromSymtab(true); Fst rightLowerContext = rcsp.getRightLowerContext(); if (rightLowerContext != null) rightLowerContext.setFromSymtab(true); // initialize Fst finalLeftContext = lib.EmptyStringLanguageFst(); Fst finalRightContext = lib.EmptyStringLanguageFst(); // these will be left as the empty string language for missing // two-level contexts if (leftUpperContext != null) { if (leftLowerContext != null) { // an overt left context in a two-level context finalLeftContext = lib.Intersect(hulden.Upper(leftUpperContext), hulden.Lower(leftLowerContext)); } else { // an overt upper left context in a one-level context finalLeftContext = hulden.Upper(leftUpperContext); } } else if (leftLowerContext != null) { finalLeftContext = hulden.Lower(leftLowerContext); } if (rightUpperContext != null) { if (rightLowerContext != null) { // an overt right context in a two-level context finalRightContext = lib.Intersect(hulden.Upper(rightUpperContext), hulden.Lower(rightLowerContext)); } else { // an overt upper right context in a one-level context finalRightContext = hulden.Upper(rightUpperContext); } } else if (rightLowerContext != null) { finalRightContext = hulden.Lower(rightLowerContext); } finalLeftContext.setFromSymtab(true); finalRightContext.setFromSymtab(true); Fst dfs = hulden.DeleteFirstSymbol(cp); // There is one context restriction context for each context in all the // parallel rules; Each context L _ DeleteFirstSymbol(CP(A,B)) R // with two-level contexts // (Lu & Ll) _ DeleteFirstSymbol(CP(A,B)) (Ru & Rl) // or now possibly CPMarkup(X,Y,Z) // both called just "cp" at this point // for missing two-level contexts, e.g. a -> b / a:b 2_2 // the right context is just the empty string language if (epenthesis) { // The rule is explicitly marked as an epenthesis rule // then add an extra constraint for epenthesis if (arrowType == RuleArrowType.RIGHT) { finalLeftContext = hulden.EPContextL_RIGHT_ARROW(finalLeftContext); finalLeftContext.setFromSymtab(true); finalRightContext = hulden.EPContextR_RIGHT_ARROW(finalRightContext); finalRightContext.setFromSymtab(true); } else { // Left-arrow rule finalLeftContext = hulden.EPContextL_LEFT_ARROW(finalLeftContext); finalLeftContext.setFromSymtab(true); finalRightContext = hulden.EPContextR_LEFT_ARROW(finalRightContext); finalRightContext.setFromSymtab(true); } } // KRB don't let OTHER in a rule context match #, the word boundary, unless // it is an epenthesis rule, in which case an example like "" -> b // with implied context "" -> b / .* _ .* , the context needs to match # // to that ""->b applied down to aaa yields bababa // if (!epenthesis) { if (finalLeftContext.getContainsOther()) { finalLeftContext.getSigma().add(symmap.putsym(hulden.ruleWordBoundarySym)); } if (finalRightContext.getContainsOther()) { finalRightContext.getSigma().add(symmap.putsym(hulden.ruleWordBoundarySym)); } } Fst leftRestrictionContext = finalLeftContext; Fst rightRestrictionContext = lib.Concat(dfs, finalRightContext); // interpRestrictionContext makes sure that the two context parts do // not contain the restDelimSym (shown as x here), and returns // leftRestrictionContext x \x* x rightRestrictionContext Fst restContextFst = interpRestrictionContext(leftRestrictionContext, rightRestrictionContext); // Union this restContextFst into the "unionOfContexts" used to compute // the "Context" Fst in Hulden's algorithm. // UnionIntoFirstInPlace() is destructive of the first argument, as desired here unionOfContexts = lib.UnionIntoFirstInPlace(unionOfContexts, restContextFst); // Now see if a constraint need to be calculated for the current context // A constraint, needed for non-optional rules, makes sure that if an // input string is found, the rule will fire. if (obligType == RuleObligType.OBLIG || matchType == RuleMatchType.MAX_L2R || matchType == RuleMatchType.MIN_L2R || matchType == RuleMatchType.MAX_R2L || matchType == RuleMatchType.MIN_R2L) { // Then we need to compute constraint(s) for the current context. // Multiple constraints are unioned together, so start with // the empty language. Fst constraintsForOneContext = lib.EmptyLanguageFst(); if (epenthesis) { // this is an epenthesis rule // KRB: debug //System.out.println("This is an epenthesis rule") ; constraintsForOneContext = lib.EmptyStringLanguageFst(); // KRB // MAX_ and MIN_ are semantically inappropriate // or confusing, I think, with a pure epenthesis rule. // Catch rules like a* {min} -> b during interpretation? } else { // not an epenthesis rule // KRB: debug //System.out.println("This is NOT an epenthesis rule.") ; // First need to compute the "difference", which // is A-0 or B-0 in Hulden's examples, e.g. // A -> B Fst inputFst = null; if (arrowType == RuleArrowType.RIGHT) { // in a right-arrow rule, the A (or X for a markup rule) is the input inputFst = (mapType == RuleMapType.MAP) ? A : X; } else { // for a left-arrow rule, the B (or X for a markup rule) is the input inputFst = (mapType == RuleMapType.MAP) ? B : X; } Fst difference = lib.Difference(inputFst, lib.EmptyStringLanguageFst()); difference.setFromSymtab(true); if (obligType == RuleObligType.OBLIG) { // Then need to compute the "Unrewritten" constraint, // which makes sure that the rule applies wherever // the context is right. // Unrewritten(A-0) or Unrewritten(B-0) in Hulden's examples, // should work for both right-arrow and left-arrow rules because // when tape 1 has @O@ ("outside"), and symbol x is on tape 2, // then tape 3 has @ID@ constraintsForOneContext = hulden.Unrewritten(difference); // suitable for matchType == RuleMatchType.ALL KRB??? constraintsForOneContext.setFromSymtab(true); } // the following possibilities are mutually exclusive if (matchType == RuleMatchType.MAX_L2R) { // need to add (union in) more constraints if (arrowType == RuleArrowType.RIGHT) { // KRB: debug //System.out.println("Union in constraints for right arrow MAX_L2R; point Z") ; constraintsForOneContext = lib.Union3Fsts(constraintsForOneContext, hulden.NotLongestRightArrow(difference), hulden.LeftmostRightArrow(difference)); } else { // KRB: debug //System.out.println("Union in constraints for left arrow MAX_L2R; point Z2") ; constraintsForOneContext = lib.Union3Fsts(constraintsForOneContext, hulden.NotLongestLeftArrow(difference), hulden.LeftmostLeftArrow(difference)); } } else if (matchType == RuleMatchType.MIN_L2R) { // need to add (union in) more constraints if (arrowType == RuleArrowType.RIGHT) { constraintsForOneContext = lib.Union3Fsts(constraintsForOneContext, hulden.ShortestRightArrow(difference), hulden.LeftmostRightArrow(difference)); } else { constraintsForOneContext = lib.Union3Fsts(constraintsForOneContext, hulden.ShortestLeftArrow(difference), hulden.LeftmostLeftArrow(difference)); } } else if (matchType == RuleMatchType.MAX_R2L) { // not implemented yet // need to add (union in) more constraints if (arrowType == RuleArrowType.RIGHT) { constraintsForOneContext = lib.Union3Fsts(constraintsForOneContext, hulden.NotLongestRightArrow(difference), hulden.RightmostRightArrow(difference)); } else { constraintsForOneContext = lib.Union3Fsts(constraintsForOneContext, hulden.NotLongestLeftArrow(difference), hulden.RightmostLeftArrow(difference)); } } else if (matchType == RuleMatchType.MIN_R2L) { // not implemented yet // need to add (union in) more constraints if (arrowType == RuleArrowType.RIGHT) { constraintsForOneContext = lib.Union3Fsts(constraintsForOneContext, hulden.ShortestRightArrow(difference), hulden.RightmostRightArrow(difference)); } else { constraintsForOneContext = lib.Union3Fsts(constraintsForOneContext, hulden.ShortestLeftArrow(difference), hulden.RightmostLeftArrow(difference)); } } } // get the "NotContain" constraint for one context Fst notContain = hulden.NotContain( lib.Concat3Fsts(finalLeftContext, constraintsForOneContext, finalRightContext)); // and intersect this constraint into the total Constraints Constraints = lib.Intersect(Constraints, notContain); } // end of block adding a constraint for a context } // end of loop through contexts } // end of loop through RuleSemanticParts // KRB: debug //System.out.println("End of loop through RuleSemanticParts") ; Fst Outside = lib.Concat3Fsts(lib.OneArcFst(hulden.outsideMarkerSym), lib.Difference(hulden.Tape2Sig(), lib.OneArcFst(hulden.ruleWordBoundarySym)), lib.OneArcFst(hulden.idMarkerSym)); Fst Base = lib.Concat3Fsts(hulden.Boundary(), lib.KleeneStar(lib.Union(Outside, BaseUnionOfCPs)), hulden.Boundary()); // surround with \x* unionOfContexts \x* Fst restrictionRhsFst = lib.Concat3Fsts(hulden.NotRestDelimStarFst(), unionOfContexts, hulden.NotRestDelimStarFst()); // IOpen => // [ Upper(L1u) & Lower(L1l) ] _ DeleteFirstSymbol(CP(A1, B1)) [ Upper(R1u) & Lower(R1l) ] , // [ Upper(L2u) & Lower(L2l) ] _ DeleteFirstSymbol(CP(A2, B2)) [ Upper(R2u) & Lower(R2l) ] ; // done slightly differently here to allow the restrictionRhsFst to be accumulated // during the loop through the contexts Fst Context = interpRestrictionExp(hulden.IOpen(), restrictionRhsFst, true); // true means that the restriction is part of the compilation of an alternation rule // The result is the intersection of Base, Context and Constraints. // Context makes sure that any rule "action" upper -> lower // is inside a valid context. // Constraints makes sure that 1) no unrewritten upper appears in a // context where it should be rewritten and 2) max or min, l2r or r2l // Optional rules ->? do not use these Constraints. Fst AlternationRule = lib.Intersect3Fsts(Base, Context, Constraints); Fst CleanAlternationRule = hulden.CleanupRule(AlternationRule); return CleanAlternationRule; } class RuleContextSemanticParts { Fst leftUpperContext; Fst leftLowerContext; Fst rightUpperContext; Fst rightLowerContext; // constructor public RuleContextSemanticParts(Fst leftUpperContext, Fst leftLowerContext, Fst rightUpperContext, Fst rightLowerContext) { this.leftUpperContext = leftUpperContext; this.leftLowerContext = leftLowerContext; this.rightUpperContext = rightUpperContext; this.rightLowerContext = rightLowerContext; } Fst getLeftUpperContext() { return leftUpperContext; } Fst getLeftLowerContext() { return leftLowerContext; } Fst getRightUpperContext() { return rightUpperContext; } Fst getRightLowerContext() { return rightLowerContext; } } class RuleSemanticParts { private RuleArrowType arrowType; private RuleObligType obligType; private RuleMatchType matchType; private RuleMapType mapType; private boolean epenthesis; // for a straightforward mapping rule, both of these will be non-null private Fst upperLhs = null; private Fst lowerLhs = null; // for a transducer rule, the following will be non-null private Fst transducerLhs = null; // a Markup rule will have both of the following non-null, // and one of the above null; when these are set (by // compileRuleInCurrentFrame) CleanupSpecialSymbolsAction // is called. private Fst leftMarkupInsertion = null; private Fst rightMarkupInsertion = null; private ArrayList<RuleContextSemanticParts> contexts; // N.B. no holding of localVarSettings (from where clauses) here. // Local var settings were made bound when each RuleSemanticParts was // instantiated // constructors public RuleSemanticParts() { epenthesis = false; upperLhs = null; lowerLhs = null; transducerLhs = null; leftMarkupInsertion = null; rightMarkupInsertion = null; contexts = null; } public RuleSemanticParts(RuleSemanticParts old) { this.setEpenthesis(old.getEpenthesis()); this.setArrowType(old.getArrowType()); this.setObligType(old.getObligType()); this.setMatchType(old.getMatchType()); this.setMapType(old.getMapType()); this.setUpperLhs(old.getUpperLhs()); this.setLowerLhs(old.getLowerLhs()); this.setTransducerLhs(old.getTransducerLhs()); this.setLeftMarkupInsertion(old.getLeftMarkupInsertion()); this.setRightMarkupInsertion(old.getRightMarkupInsertion()); this.setContexts(old.getContexts()); } // end constructors void setArrowType(RuleArrowType arrowType) { this.arrowType = arrowType; } void setObligType(RuleObligType obligType) { this.obligType = obligType; } void setMatchType(RuleMatchType matchType) { this.matchType = matchType; } void setMapType(RuleMapType mapType) { this.mapType = mapType; } void setEpenthesis(boolean val) { this.epenthesis = val; } void setUpperLhs(Fst upperLhs) { this.upperLhs = upperLhs; } void setLowerLhs(Fst lowerLhs) { this.lowerLhs = lowerLhs; } void setTransducerLhs(Fst transducerLhs) { this.transducerLhs = transducerLhs; } void setLeftMarkupInsertion(Fst leftMarkupInsertion) { this.leftMarkupInsertion = leftMarkupInsertion; } void setRightMarkupInsertion(Fst rightMarkupInsertion) { this.rightMarkupInsertion = rightMarkupInsertion; } void setContexts(ArrayList<RuleContextSemanticParts> contexts) { this.contexts = contexts; } RuleArrowType getArrowType() { return arrowType; } RuleObligType getObligType() { return obligType; } RuleMatchType getMatchType() { return matchType; } RuleMapType getMapType() { return mapType; } boolean getEpenthesis() { return epenthesis; } Fst getUpperLhs() { return upperLhs; } Fst getLowerLhs() { return lowerLhs; } Fst getTransducerLhs() { return transducerLhs; } Fst getLeftMarkupInsertion() { return leftMarkupInsertion; } Fst getRightMarkupInsertion() { return rightMarkupInsertion; } ArrayList<RuleContextSemanticParts> getContexts() { return contexts; } } ArrayList<RuleContextSemanticParts> compileRuleSyntacticContexts(RuleSyntacticParts rsynp, InterpData data) { // Handle the set of contexts for one rule. // Each syntactic context gets translated into one semantic context // // Get the ArrayList of RuleContextSyntacticParts from the RuleSyntacticParts // object. Each RuleContextSyntacticParts contains two unevaluated ASTs: // leftRuleContext and rightRuleContext (each can be null) and levels (ONE or TWO) ArrayList<RuleContextSyntacticParts> contexts = rsynp.getContexts(); // the result will be a parallel ArrayList of RuleContextSemanticParts ArrayList<RuleContextSemanticParts> result = new ArrayList<RuleContextSemanticParts>(); // the arrowType is the same for the whole rule RuleArrowType arrowType = rsynp.getArrowType(); if (contexts == null) { // then the original rule looks like a -> b with no overt context(s) at all, // equivalent to // a -> b / _ a <- b / _ // or // a -> b / "" _ "" a <- b / "" _ "" // These last forms are what we need to model here for Hulden's algorithms. // Always pretend that there is one one-level context with empty-string left and right if (arrowType == RuleArrowType.RIGHT) { // order of arguments is leftUpper, leftLower, rightUpper, rightLower // set the upper contexts to the empty string language result.add(new RuleContextSemanticParts(lib.EmptyStringLanguageFst(), null, lib.EmptyStringLanguageFst(), null)); } else { // for a left-arrow rule // set the lower contexts to the empty string language result.add(new RuleContextSemanticParts(null, lib.EmptyStringLanguageFst(), null, lib.EmptyStringLanguageFst())); } // and we're done return result; } // Reaching here, there was at least one overt syntactic context, // but it could be empty like // a -> b / _ or a <- b / _ // or partially empty like a -> b / l _ or a -> b / _ r // or a -> b / l _ r for (Iterator<RuleContextSyntacticParts> iter = contexts.iterator(); iter.hasNext();) { RuleContextSyntacticParts context = iter.next(); // each context can be one-level or two-level RuleContextLevels levels = context.getLevels(); // default, start by setting all 4 semantic context parts (FSTs) to null Fst leftUpperContext = null; Fst leftLowerContext = null; Fst rightUpperContext = null; Fst rightLowerContext = null; // In the syntax, the right and left context ASTs are just regexps; // We don't know if they're acceptors or transducers until // they're evaluated. ASTleft_rule_context leftRuleContextAst = context.getLeftRuleContext(); ASTright_rule_context rightRuleContextAst = context.getRightRuleContext(); if (leftRuleContextAst == null && rightRuleContextAst == null) { // overt but empty context, e.g. a -> b / _ or a <- b / _ // Pretend that we have a -> b / "" _ "" or a <- b / "" _ "" // If the context is two-level, leave the nulls // but if the context is one-level, then insert the empty string languages if (levels == RuleContextLevels.ONE) { if (rsynp.getArrowType() == RuleArrowType.RIGHT) { // set the upper contexts to the empty string language result.add(new RuleContextSemanticParts(lib.EmptyStringLanguageFst(), null, lib.EmptyStringLanguageFst(), null)); } else { // for a left-arrow rule // set the lower contexts to the empty string language result.add(new RuleContextSemanticParts(null, lib.EmptyStringLanguageFst(), null, lib.EmptyStringLanguageFst())); } } continue; } if (leftRuleContextAst != null) { // then evaluate it leftRuleContextAst.jjtAccept(this, data); Fst leftContext = (Fst) stack.pop(); // may be acceptor or transducer leftContext.setFromSymtab(true); // needed if TWO levels and need to extract projections if (levels == RuleContextLevels.ONE) { if (!lib.IsSemanticAcceptor(leftContext)) { throw new RuleSemanticException( "In a one-level context, the left context evaluated as a Transducer."); } if (arrowType == RuleArrowType.RIGHT) { // then the indicated context matches only on the upper side leftUpperContext = leftContext; } else { // for <- rule, the indicated context matches only on // the lower side leftLowerContext = leftContext; } } else { // two-level context, e.g. a:b // split it into upper and lower leftUpperContext = lib.InputProjection(leftContext); leftLowerContext = lib.OutputProjection(leftContext); } } else { // the left context is empty: a -> b / _ r or a <- b / _ r // Handle it like a -> b / "" _ r or a <- b / "" _ r // If it's a two-level context, just leave leftUpperContext and // leftLowerContext set to null for Hulden's algorithms if (levels == RuleContextLevels.ONE) { if (arrowType == RuleArrowType.RIGHT) { leftUpperContext = lib.EmptyStringLanguageFst(); } else { // for a left-arrow rule leftLowerContext = lib.EmptyStringLanguageFst(); } } } if (rightRuleContextAst != null) { // then evaluate it rightRuleContextAst.jjtAccept(this, data); Fst rightContext = (Fst) stack.pop(); // may be acceptor or transducer rightContext.setFromSymtab(true); // needed if TWO levels and need to extract projections if (levels == RuleContextLevels.ONE) { if (!lib.IsSemanticAcceptor(rightContext)) { throw new RuleSemanticException( "In a one-level context, the right context evaluated as a Transducer."); } if (arrowType == RuleArrowType.RIGHT) { // then the context matches only on the upper side rightUpperContext = rightContext; } else { // for <- rule, the context matches only on the lower side rightLowerContext = rightContext; } } else { // split the Fst context into upper and lower rightUpperContext = lib.InputProjection(rightContext); rightLowerContext = lib.OutputProjection(rightContext); } } else { // the right context is empty: a -> b / l _ or a <- b / l _ // Handle it like a -> b / l _ "" or a <-b / l _ "" // If it's a two-level context, just leave leftUpperContext and // leftLowerContext set to null for Hulden's algorithms if (levels == RuleContextLevels.ONE) { if (arrowType == RuleArrowType.RIGHT) { rightUpperContext = lib.EmptyStringLanguageFst(); } else { rightLowerContext = lib.EmptyStringLanguageFst(); } } } // make sure that none of the context parts contains special // characters used when compiling the rules Fst Lu = leftUpperContext; if (Lu != null) { Lu = hulden.CleanupSpecialSymbolsContext(Lu); } //Lu.setFromSymtab(true) ; Fst Ll = leftLowerContext; if (Ll != null) { Ll = hulden.CleanupSpecialSymbolsContext(Ll); } //Ll.setFromSymtab(true) ; Fst Ru = rightUpperContext; if (Ru != null) { Ru = hulden.CleanupSpecialSymbolsContext(Ru); } //Ru.setFromSymtab(true) ; Fst Rl = rightLowerContext; if (Rl != null) { Rl = hulden.CleanupSpecialSymbolsContext(Rl); } //Rl.setFromSymtab(true) ; result.add(new RuleContextSemanticParts(Lu, Ll, Ru, Rl)); } return result; } ArrayList<RuleSemanticParts> compileRuleInCurrentFrame(RuleSyntacticParts rsynp, InterpData data) { // Don't worry about the where clauses (localVarSettings in the rsynp) here; // This method is called from compileRuleSyntacticParts, which takes care of // the where clauses and setting the local variables // (one set of them at a time) // Usually return one RuleSemanticParts object. // But if the input expression (upper side in a right-arrow rule, lower side in a // left-arrow rule) can match the empty string, and is not just the empty // string; split the original rule into two rules, one of them an epenthesis // rule, then return two RuleSemanticParts. RuleArrowType arrowType = rsynp.getArrowType(); // RIGHT or LEFT RuleObligType obligType = rsynp.getObligType(); // OBLIG or OPT RuleMatchType matchType = rsynp.getMatchType(); // ALL, MAX_L2R, MIN_L2R, MAX_R2L, MIN_R2L RuleMapType mapType = rsynp.getMapType(); // MAP or MARKUP // the return value, start with an empty List ArrayList<RuleSemanticParts> oneOrTwoRuleSemanticParts = new ArrayList<RuleSemanticParts>(); // There's always one RuleSemanticParts returned, here called the // "basicRuleSemanticParts." If the input expression matches the // empty string, and does not denote _just_ the empty string, // then a second RuleSemanticParts is returned as well RuleSemanticParts basicRuleSemanticParts = new RuleSemanticParts(); basicRuleSemanticParts.setArrowType(arrowType); basicRuleSemanticParts.setObligType(obligType); basicRuleSemanticParts.setMatchType(matchType); basicRuleSemanticParts.setMapType(mapType); // Handle the LHS if (mapType == RuleMapType.MARKUP) { // Markup Rule; either the left insertion xor the right insertion // could be syntactically missing Fst leftMarkupInsertion = null; if (rsynp.getLeftMarkupInsertion() != null) { rsynp.getLeftMarkupInsertion().jjtAccept(this, data); leftMarkupInsertion = (Fst) stack.pop(); if (!lib.IsSemanticAcceptor(leftMarkupInsertion)) { throw new RuleSemanticException( "The left-side insertion of a markup rule compiled into a Transducer. An Acceptor is required."); } } else { // for missing left insertion, interpret as the empty string leftMarkupInsertion = lib.EmptyStringLanguageFst(); } Fst rightMarkupInsertion = null; if (rsynp.getRightMarkupInsertion() != null) { rsynp.getRightMarkupInsertion().jjtAccept(this, data); rightMarkupInsertion = (Fst) stack.pop(); if (!lib.IsSemanticAcceptor(rightMarkupInsertion)) { throw new RuleSemanticException( "The right-side insertion of a markup rule compiled into a Transducer. An Acceptor is required."); } } else { // for missing right insertion, interpret as the empty string rightMarkupInsertion = lib.EmptyStringLanguageFst(); } basicRuleSemanticParts.setLeftMarkupInsertion(hulden.CleanupSpecialSymbolsAction(leftMarkupInsertion)); basicRuleSemanticParts .setRightMarkupInsertion(hulden.CleanupSpecialSymbolsAction(rightMarkupInsertion)); } // initialize Fst upperLhs = null; Fst lowerLhs = null; Fst transducerLhs = null; if (rsynp.getLhsTransducer() != null) { // this is a transducer rule, e.g. a:b -> / ... // where the mapping is expressed in the syntax as a transducer. // .getLhsTransducer() returns an ASTrule_lhs_transducer, which // has one child, some kind of regexp denoting a transducer rsynp.getLhsTransducer().jjtGetChild(0).jjtAccept(this, data); // leaves an Fst on the stack transducerLhs = (Fst) stack.pop(); transducerLhs = hulden.CleanupSpecialSymbolsAction(transducerLhs); // .setFromSymtab because, for an oblig rule, the upper or lower language // will need to be extracted transducerLhs.setFromSymtab(true); basicRuleSemanticParts.setTransducerLhs(transducerLhs); } else { // this is a mapping rule (with separately specified upperLhs and LowerLhs), // or a markup rule, in which case one of the following still needs to be set if (rsynp.getLhsUpper() != null) { rsynp.getLhsUpper().jjtAccept(this, data); // leaves an Fst on the stack upperLhs = (Fst) stack.pop(); if (!lib.IsSemanticAcceptor(upperLhs)) { throw new RuleSemanticException( "The upper left-hand-side expression in an alternation rule compiled into a Transducer. An Acceptor is required."); } basicRuleSemanticParts.setUpperLhs(hulden.CleanupSpecialSymbolsAction(upperLhs)); } if (rsynp.getLhsLower() != null) { rsynp.getLhsLower().jjtAccept(this, data); lowerLhs = (Fst) stack.pop(); if (!lib.IsSemanticAcceptor(lowerLhs)) { throw new RuleSemanticException( "The lower left-hand-side expression in an alternation rule compiled into a Transducer. An Acceptor is required."); } basicRuleSemanticParts.setLowerLhs(hulden.CleanupSpecialSymbolsAction(lowerLhs)); } } // compile the RHS (contexts) basicRuleSemanticParts.setContexts(compileRuleSyntacticContexts(rsynp, data)); // check for epenthesis in the rule, need to decide which // is the "input" side: upper for a right-arrow rule, // and lower for a left-arrow rule Fst inputFst; if (arrowType == RuleArrowType.RIGHT) { // the input side is the upper side for a right-arrow rule if (transducerLhs != null) { // this is a transducer rule like a:b -> / l _ r inputFst = lib.InputProjection(transducerLhs); } else { // this is a normal mapping rule like a -> b / l _ r inputFst = upperLhs; } } else { // the input side is the lower side for a left-arrow rule if (transducerLhs != null) { // this is a transducer rule inputFst = lib.OutputProjection(transducerLhs); } else { inputFst = lowerLhs; } } if (lib.ContainsEmptyString(inputFst)) { // if the input expression can match the empty string // KRB: debug //System.out.println("Input exp contains the empty string") ; // for now, at least, disallow epsilon in the input for transducer rules //if ( transducerLhs != null) { // throw new RuleSemanticException("The input expression in the transducer rule can match the empty string") ; //} // for now, at least, disallow epsilon in the input for {max} and {min} rules if ((matchType == RuleMatchType.MAX_L2R) || (matchType == RuleMatchType.MIN_L2R) || (matchType == RuleMatchType.MAX_R2L) || (matchType == RuleMatchType.MIN_R2L)) { throw new RuleSemanticException( "The input expression to the {max} or {min} rule can match the empty string. Report your intentions to the developer."); } // either compile the rule as an epenthesis rule, OR // extract out the epenthesis as a separate rule to be compiled in parallel if (lib.IsEmptyStringLanguage(inputFst)) { // then this rule is a straightforward epenthesis rule like "" -> b basicRuleSemanticParts.setEpenthesis(true); } else { // this is a rule like a* -> b, where the input side // can match the empty string, and more // or it's a transducer rule like a*:b -> / // or (a*:b) | c:d | e:f -> / // // For non-transducer rule // Modify the rule to be (a* - "") -> b // and // create a new parallel RuleSemanticParts for the epenthesis // rule "" -> b // For transducer rules, // create a new "parallel" epenthesis rule that has only the empty string language on the input side // start with a copy of the basic rule; mark it explicitly as an epenthesis rule RuleSemanticParts epenthesisRuleSemanticParts = new RuleSemanticParts(basicRuleSemanticParts); epenthesisRuleSemanticParts.setEpenthesis(true); // for TRANSDUCER RULES if (transducerLhs != null) { Fst newTransducerLhs; // new (modified) LHS for the basic transducer rule // remove the epsilon from the basic transducer LHS, using _composition_ if (arrowType == RuleArrowType.RIGHT) { // the epsilon is on the upper side; // compose the complement of the empty-string language on the upper side newTransducerLhs = hulden.CleanupSpecialSymbolsAction( lib.Compose(lib.Complement(lib.EmptyStringLanguageFst()), transducerLhs)); } else { // the epsilon is on the lower side; // compose the complement of the empty-string language on the lower side newTransducerLhs = hulden.CleanupSpecialSymbolsAction( lib.Compose(transducerLhs, lib.Complement(lib.EmptyStringLanguageFst()))); } basicRuleSemanticParts.setTransducerLhs(newTransducerLhs); // the epenthesis is now removed from the input side of the basic rule if (arrowType == RuleArrowType.RIGHT) { // in the epenthesisRuleSemanticParts, make the upper side the empty-string language epenthesisRuleSemanticParts.setTransducerLhs(hulden.CleanupSpecialSymbolsAction( lib.Compose(lib.EmptyStringLanguageFst(), transducerLhs))); } else { // in the epenthesisRuleSemanticParts, make the lower side the empty-string language epenthesisRuleSemanticParts.setTransducerLhs(hulden.CleanupSpecialSymbolsAction( lib.Compose(transducerLhs, lib.EmptyStringLanguageFst()))); } } else { // In basicRuleSemanticParts, _subtract_ the empty string from the // LHS input expression (modify the inputFst for the basic rule) Fst newInputFst = hulden .CleanupSpecialSymbolsAction(lib.Difference(inputFst, lib.EmptyStringLanguageFst())); // modify the basic rule if (arrowType == RuleArrowType.RIGHT) { basicRuleSemanticParts.setUpperLhs(newInputFst); } else { basicRuleSemanticParts.setLowerLhs(newInputFst); } // reset the input LHS expression to the empty string language if (arrowType == RuleArrowType.RIGHT) { epenthesisRuleSemanticParts.setUpperLhs(lib.EmptyStringLanguageFst()); } else { epenthesisRuleSemanticParts.setLowerLhs(lib.EmptyStringLanguageFst()); } } // add oneOrTwoRuleSemanticParts.add(epenthesisRuleSemanticParts); } } oneOrTwoRuleSemanticParts.add(basicRuleSemanticParts); return oneOrTwoRuleSemanticParts; } ArrayList<RuleSemanticParts> compileRuleSyntacticParts(RuleSyntacticParts rsynp, InterpData data) { // input is one RuleSyntacticParts object, representing one syntactic Rule AST. // One RuleSyntacticParts object can result in multiple RuleSemanticParts // objects because of where clauses and/or epenthesis (which effectively translate // into multiple rules to be compiled in parallel). The RuleSemanticParts(s) are // returned as an ArrayList<RuleSemanticParts> ArrayList<RuleSemanticParts> result = new ArrayList<RuleSemanticParts>(); // get the local variables settings (if any, originally from 'where' clauses) ArrayList<ArrayList<RuleLocalVarSetting>> localVarSettings = rsynp.getLocalVarSettings(); if (localVarSettings != null) { // There are local rule-var settings from where clauses. // // The variable settings will be done in a new Frame to prevent interference // with existing variables in the current and higher frames. env.allocateFrame(); // Push to a new Frame here // (see env.releaseFrame() below) ArrayList<RuleLocalVarSetting> oneSetOfSettings = null; for (int i = 0; i < localVarSettings.size(); i++) { oneSetOfSettings = localVarSettings.get(i); // set each of the local variables in the new Frame for (int j = 0; j < oneSetOfSettings.size(); j++) { String varName = oneSetOfSettings.get(j).getVarName(); Fst fstValue = oneSetOfSettings.get(j).getFstValue(); env.put(varName, fstValue); } // now compile the rule with these settings // If the input expression can match the empty string, // then the compilation will result in two RuleSemanticParts objects because // the epenthesis part is split into a separate parallel rule ArrayList<RuleSemanticParts> oneOrTwoRuleSemanticParts = compileRuleInCurrentFrame(rsynp, data); // add the RuleSemanticParts from these variable settings to the result result.addAll(oneOrTwoRuleSemanticParts); } // release the Frame used for setting the 'where' variables env.releaseFrame(); // Pop back to orig frame here } else { // simple case; no local rule variables from where clauses, but there could // still be an effective translation to two parallel rules if epenthesis is // possible (if the input expression can match the empty string) ArrayList<RuleSemanticParts> oneOrTwoRuleSemanticParts = compileRuleInCurrentFrame(rsynp, data); // add the RuleSemanticParts from these variable settings to the result result.addAll(oneOrTwoRuleSemanticParts); } return result; } public Object visit(ASTrule_right_arrow_oblig node, Object data) { // The most commonly used kind of alternation rule // Simplest form: // a -> b // With optional context(s) // a -> b / left _ right // a -> b / l1 _ r1 || l2 _ r2 ... (one rule with multiple contexts) // With optional where clause(s) // a -> b { where ... } // a -> b / l _ r { where ...} (there can be multiple where clauses) // // rule_right_arrow_oblig can also be a right-arrow Markup Rule with // X -> Y ... Z / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // evaluate it; see visit(ASTrule_lhs... node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts ruleSyntacticParts = (RuleSyntacticParts) stack.pop(); // each rule AST (each syntactic rule) has its "parts" stored in one // RuleSyntacticParts object // set what we know from the rule arrow ruleSyntacticParts.setArrowType(RuleArrowType.RIGHT); // right-arrow, // not left-arrow ruleSyntacticParts.setObligType(RuleObligType.OBLIG); // oblig not optional ruleSyntacticParts.setMatchType(RuleMatchType.ALL); // not max or min // Collect the rule parts from the rest of the AST, if any, starting // at Child(1). (Child(0), the rule_lhs, was just handled above) // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack ruleSyntacticParts.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack ruleSyntacticParts.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the // local var settings for the where-clauses // now convert the one RuleSemanticParts object into a _list_ of // RuleSemanticParts objects. Can be one to many because of where-clauses // and/or epenthesis effectively translate into multiple rules that need // to be compiled in parallel. ArrayList<RuleSemanticParts> listOfRuleSemanticParts = compileRuleSyntacticParts(ruleSyntacticParts, (InterpData) data); // If the mother node is ASTnet_parallel_func_call (i.e. the rule was written // inside $^parallel(...), just push the listOfRuleSemanticParts. // But if not, then convert the listOfRuleSemanticParts into an Fst // and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { // then just push the listOfRuleSemanticParts on the stack, // to be popped off and used by net_parallel_func_call, which // will call compileRuleSemanticParts stack.push(listOfRuleSemanticParts); } else { // call compileRuleSemanticParts directly, and push the resulting // single Fst on the stack stack.push(compileRuleSemanticParts(listOfRuleSemanticParts)); } return data; } public Object visit(ASTrule_transducer_right_arrow_oblig node, Object data) { // a:b -> / left_right // a:b -> / l _ r { where ...} (there can be multiple where clauses) // cannot be a Markup rule int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs_transducer only // if 2, then either // rule_lhs_transducer and rule_rhs // or rule_lhs_transducer and where_clauses // if 3, then rule_lhs_transducer, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts ruleSyntacticParts = (RuleSyntacticParts) stack.pop(); // each rule AST (each syntactic rule) has its "parts" stored in one // RuleSyntacticParts object // set what we know from the rule arrow ruleSyntacticParts.setArrowType(RuleArrowType.RIGHT); // right-arrow, // not left-arrow ruleSyntacticParts.setObligType(RuleObligType.OBLIG); // oblig not optional ruleSyntacticParts.setMatchType(RuleMatchType.ALL); // not max or min // Collect the rule parts from the rest of the AST, if any, starting // at Child(1). (Child(0), the rule_lhs, was just handled above) // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack ruleSyntacticParts.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack ruleSyntacticParts.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the // local var settings for the where-clauses // now convert the one RuleSemanticParts object into a _list_ of // RuleSemanticParts objects. Can be one to many because of where-clauses // and/or epenthesis effectively translate into multiple rules that need // to be compiled in parallel. ArrayList<RuleSemanticParts> listOfRuleSemanticParts = compileRuleSyntacticParts(ruleSyntacticParts, (InterpData) data); // If the mother node is ASTnet_parallel_func_call (i.e. the rule was written // inside $^parallel(...), just push the listOfRuleSemanticParts. // But if not, then convert the listOfRuleSemanticParts into an Fst // and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { // then just push the listOfRuleSemanticParts on the stack, // to be popped off and used by net_parallel_func_call, which // will call compileRuleSemanticParts stack.push(listOfRuleSemanticParts); } else { // call compileRuleSemanticParts directly, and push the resulting // single Fst on the stack stack.push(compileRuleSemanticParts(listOfRuleSemanticParts)); } return data; } public Object visit(ASTrule_transducer_left_arrow_oblig node, Object data) { // a:b <- / left_right // a:b <- / l _ r { where ...} (there can be multiple where clauses) // cannot be a Markup rule int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs_transducer only // if 2, then either // rule_lhs_transducer and rule_rhs // or rule_lhs_transducer and where_clauses // if 3, then rule_lhs_transducer, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts ruleSyntacticParts = (RuleSyntacticParts) stack.pop(); // each rule AST (each syntactic rule) has its "parts" stored in one // RuleSyntacticParts object // set what we know from the rule arrow ruleSyntacticParts.setArrowType(RuleArrowType.LEFT); // left-arrow, // not right-arrow ruleSyntacticParts.setObligType(RuleObligType.OBLIG); // oblig not optional ruleSyntacticParts.setMatchType(RuleMatchType.ALL); // not max or min // Collect the rule parts from the rest of the AST, if any, starting // at Child(1). (Child(0), the rule_lhs, was just handled above) // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack ruleSyntacticParts.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack ruleSyntacticParts.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the // local var settings for the where-clauses // now convert the one RuleSemanticParts object into a _list_ of // RuleSemanticParts objects. Can be one to many because of where-clauses // and/or epenthesis effectively translate into multiple rules that need // to be compiled in parallel. ArrayList<RuleSemanticParts> listOfRuleSemanticParts = compileRuleSyntacticParts(ruleSyntacticParts, (InterpData) data); // If the mother node is ASTnet_parallel_func_call (i.e. the rule was written // inside $^parallel(...), just push the listOfRuleSemanticParts. // But if not, then convert the listOfRuleSemanticParts into an Fst // and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { // then just push the listOfRuleSemanticParts on the stack, // to be popped off and used by net_parallel_func_call, which // will call compileRuleSemanticParts stack.push(listOfRuleSemanticParts); } else { // call compileRuleSemanticParts directly, and push the resulting // single Fst on the stack stack.push(compileRuleSemanticParts(listOfRuleSemanticParts)); } return data; } public Object visit(ASTrule_right_arrow_opt node, Object data) { // The most commonly used kind of OPTIONAL alternation rule // a ->? b // With optional context(s) // a ->? b / left _ right // a ->? b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a -> b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X ->? Y ... Z int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.ALL); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_right_arrow_opt node, Object data) { // a:b ->? / left_right // a:b ->? / l _ r { where ...} (there can be multiple where clauses) // cannot be a Markup rule int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs_transducer only // if 2, then either // rule_lhs_transducer and rule_rhs // or rule_lhs_transducer and where_clauses // if 3, then rule_lhs_transducer, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts ruleSyntacticParts = (RuleSyntacticParts) stack.pop(); // each rule AST (each syntactic rule) has its "parts" stored in one // RuleSyntacticParts object // set what we know from the rule arrow ruleSyntacticParts.setArrowType(RuleArrowType.RIGHT); // right-arrow, // not left-arrow ruleSyntacticParts.setObligType(RuleObligType.OPT); // opt not oblig ruleSyntacticParts.setMatchType(RuleMatchType.ALL); // not max or min // Collect the rule parts from the rest of the AST, if any, starting // at Child(1). (Child(0), the rule_lhs, was just handled above) // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack ruleSyntacticParts.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack ruleSyntacticParts.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the // local var settings for the where-clauses // now convert the one RuleSemanticParts object into a _list_ of // RuleSemanticParts objects. Can be one to many because of where-clauses // and/or epenthesis effectively translate into multiple rules that need // to be compiled in parallel. ArrayList<RuleSemanticParts> listOfRuleSemanticParts = compileRuleSyntacticParts(ruleSyntacticParts, (InterpData) data); // If the mother node is ASTnet_parallel_func_call (i.e. the rule was written // inside $^parallel(...), just push the listOfRuleSemanticParts. // But if not, then convert the listOfRuleSemanticParts into an Fst // and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { // then just push the listOfRuleSemanticParts on the stack, // to be popped off and used by net_parallel_func_call, which // will call compileRuleSemanticParts stack.push(listOfRuleSemanticParts); } else { // call compileRuleSemanticParts directly, and push the resulting // single Fst on the stack stack.push(compileRuleSemanticParts(listOfRuleSemanticParts)); } return data; } public Object visit(ASTrule_transducer_left_arrow_opt node, Object data) { // a:b <-? / left_right // a:b <-? / l _ r { where ...} (there can be multiple where clauses) // cannot be a Markup rule int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs_transducer only // if 2, then either // rule_lhs_transducer and rule_rhs // or rule_lhs_transducer and where_clauses // if 3, then rule_lhs_transducer, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts ruleSyntacticParts = (RuleSyntacticParts) stack.pop(); // each rule AST (each syntactic rule) has its "parts" stored in one // RuleSyntacticParts object // set what we know from the rule arrow ruleSyntacticParts.setArrowType(RuleArrowType.LEFT); // left-arrow, // not right-arrow ruleSyntacticParts.setObligType(RuleObligType.OPT); // opt not oblig ruleSyntacticParts.setMatchType(RuleMatchType.ALL); // not max or min // Collect the rule parts from the rest of the AST, if any, starting // at Child(1). (Child(0), the rule_lhs, was just handled above) // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack ruleSyntacticParts.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack ruleSyntacticParts.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the // local var settings for the where-clauses // now convert the one RuleSemanticParts object into a _list_ of // RuleSemanticParts objects. Can be one to many because of where-clauses // and/or epenthesis effectively translate into multiple rules that need // to be compiled in parallel. ArrayList<RuleSemanticParts> listOfRuleSemanticParts = compileRuleSyntacticParts(ruleSyntacticParts, (InterpData) data); // If the mother node is ASTnet_parallel_func_call (i.e. the rule was written // inside $^parallel(...), just push the listOfRuleSemanticParts. // But if not, then convert the listOfRuleSemanticParts into an Fst // and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { // then just push the listOfRuleSemanticParts on the stack, // to be popped off and used by net_parallel_func_call, which // will call compileRuleSemanticParts stack.push(listOfRuleSemanticParts); } else { // call compileRuleSemanticParts directly, and push the resulting // single Fst on the stack stack.push(compileRuleSemanticParts(listOfRuleSemanticParts)); } return data; } public Object visit(ASTrule_right_arrow_max_l2r_oblig node, Object data) { // a {max}-> b or a {maxl2r}-> b (equivalent) // With optional context(s) // a {max}-> b / left _ right // a {max}-> b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a {max}-> b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X {max}-> Y ~~~ Z / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.MAX_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_right_arrow_max_l2r_oblig node, Object data) { // <transducer> {max} -> / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.MAX_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_right_arrow_min_l2r_oblig node, Object data) { // <transducer> {max} -> / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.MIN_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_right_arrow_min_l2r_opt node, Object data) { // <transducer> {max} -> / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.MIN_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_right_arrow_max_l2r_opt node, Object data) { // a {max}->? b or a {maxl2r}->? b (equivalent) // With optional context(s) // a {max}->? b / left _ right // a {max}->? b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a {max}->? b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X {max}->? Y ~~~ Z / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.MAX_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_right_arrow_max_l2r_opt node, Object data) { // <transducer> {max} ->? / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.MAX_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } /* public Object visit(ASTrule_right_arrow_max_r2l_oblig node, Object data) { // a {maxr2l}-> b // With optional context(s) // a {maxr2l}-> b / left _ right // a {maxr2l}-> b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a {maxr2l}-> b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X {maxr2l}-> Y ~~~ Z / ... int daughterCount = node.jjtGetNumChildren() ; // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data) ; // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts)stack.pop() ; // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT) ; rsynpt.setObligType(RuleObligType.OBLIG) ; rsynpt.setMatchType(RuleMatchType.MAX_R2L) ; // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i) ; node.jjtGetChild(i).jjtAccept(this, data) ; if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>)stack.pop()) ; } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>)stack.pop()) ; } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data) ; // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent() ; if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts) ; } else { stack.push(compileRuleSemanticParts(semanticParts)) ; } return data ; } */ /* public Object visit(ASTrule_right_arrow_max_r2l_opt node, Object data) { // a {maxr2l}->? b // With optional context(s) // a {maxr2l}->? b / left _ right // a {maxr2l}->? b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a {maxr2l}->? b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X {maxr2l}-> Y ~~~ Z / ... int daughterCount = node.jjtGetNumChildren() ; // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data) ; // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts)stack.pop() ; // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT) ; rsynpt.setObligType(RuleObligType.OPT) ; rsynpt.setMatchType(RuleMatchType.MAX_R2L) ; // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i) ; node.jjtGetChild(i).jjtAccept(this, data) ; if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>)stack.pop()) ; } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>)stack.pop()) ; } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data) ; // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent() ; if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts) ; } else { stack.push(compileRuleSemanticParts(semanticParts)) ; } return data ; } */ public Object visit(ASTrule_right_arrow_min_l2r_oblig node, Object data) { // a {min}-> b or a {minl2r}-> b (equivalent) // With optional context(s) // a {min}-> b / left _ right // a {min}-> b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a {min}-> b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X {min}-> Y .. Z / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.MIN_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_right_arrow_min_l2r_opt node, Object data) { // a {min}->? b or a {minl2r}-> b (equivalent) // With optional context(s) // a {min}->? b / left _ right // a {min}->? b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a {min}->? b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X {min}-> Y .. Z / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.MIN_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } /* public Object visit(ASTrule_right_arrow_min_r2l_oblig node, Object data) { // a {minr2l}-> b // With optional context(s) // a {minr2l}-> b / left _ right // a {minr2l}-> b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a {minr2l}-> b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X {minr2l}-> Y .. Z / ... int daughterCount = node.jjtGetNumChildren() ; // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data) ; // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts)stack.pop() ; // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT) ; rsynpt.setObligType(RuleObligType.OBLIG) ; rsynpt.setMatchType(RuleMatchType.MIN_R2L) ; // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i) ; node.jjtGetChild(i).jjtAccept(this, data) ; if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>)stack.pop()) ; } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>)stack.pop()) ; } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data) ; // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent() ; if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts) ; } else { stack.push(compileRuleSemanticParts(semanticParts)) ; } return data ; } */ /* public Object visit(ASTrule_right_arrow_min_r2l_opt node, Object data) { // a {minr2l}->? b // With optional context(s) // a {minr2l}->? b / left _ right // a {minr2l}->? b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a {minr2l}->? b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule X {minr2l}-> Y .. Z / ... int daughterCount = node.jjtGetNumChildren() ; // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data) ; // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts)stack.pop() ; // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.RIGHT) ; rsynpt.setObligType(RuleObligType.OPT) ; rsynpt.setMatchType(RuleMatchType.MIN_R2L) ; // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i) ; node.jjtGetChild(i).jjtAccept(this, data) ; if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>)stack.pop()) ; } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>)stack.pop()) ; } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data) ; // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent() ; if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts) ; } else { stack.push(compileRuleSemanticParts(semanticParts)) ; } return data ; } */ public Object visit(ASTrule_left_arrow_oblig node, Object data) { // a <- b // With optional context(s) // a <- b / left _ right // a <- b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <- b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <- X / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.ALL); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_left_arrow_opt node, Object data) { // a <-? b // With optional context(s) // a <-? b / left _ right // a <-? b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-? b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-? X int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.ALL); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_left_arrow_max_l2r_oblig node, Object data) { // a <-{max} b or a <-{maxl2r} (equivalent) // With optional context(s) // a <-{max} b / left _ right // a <-{max} b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-{max} b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-{max} X / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.MAX_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_left_arrow_max_l2r_oblig node, Object data) { // <- {max} <transducer> / int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.MAX_L2R); // Collect the rule parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_left_arrow_min_l2r_oblig node, Object data) { // <transducer> {max} <- / int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.MIN_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_left_arrow_max_l2r_opt node, Object data) { // a <-? {max} b or a <-{maxl2r} (equivalent) // With optional context(s) // a <-? {max} b / left _ right // a <-? {max} b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-? {max} b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-? {max} X / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.MAX_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_left_arrow_max_l2r_opt node, Object data) { // <transducer> {max} <-? / int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.MAX_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_transducer_left_arrow_min_l2r_opt node, Object data) { // <transducer> {max} <-? / int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs_transducer as the 0th child, // evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.MIN_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } /* public Object visit(ASTrule_left_arrow_max_r2l_oblig node, Object data) { // a <-{maxr2l} // With optional context(s) // a <-{maxr2l} b / left _ right // a <-{maxr2l} b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-{maxr2l} b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-{maxr2l} X / ... int daughterCount = node.jjtGetNumChildren() ; // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data) ; // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts)stack.pop() ; // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT) ; rsynpt.setObligType(RuleObligType.OBLIG) ; rsynpt.setMatchType(RuleMatchType.MAX_R2L) ; // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i) ; node.jjtGetChild(i).jjtAccept(this, data) ; if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>)stack.pop()) ; } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>)stack.pop()) ; } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data) ; // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent() ; if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts) ; } else { stack.push(compileRuleSemanticParts(semanticParts)) ; } return data ; } */ /* public Object visit(ASTrule_left_arrow_max_r2l_opt node, Object data) { // a <-? {maxr2l} // With optional context(s) // a <-? {maxr2l} b / left _ right // a <-? {maxr2l} b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-? {maxr2l} b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-? {maxr2l} X / ... int daughterCount = node.jjtGetNumChildren() ; // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data) ; // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts)stack.pop() ; // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT) ; rsynpt.setObligType(RuleObligType.OPT) ; rsynpt.setMatchType(RuleMatchType.MAX_R2L) ; // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i) ; node.jjtGetChild(i).jjtAccept(this, data) ; if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>)stack.pop()) ; } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>)stack.pop()) ; } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data) ; // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent() ; if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts) ; } else { stack.push(compileRuleSemanticParts(semanticParts)) ; } return data ; } */ public Object visit(ASTrule_left_arrow_min_l2r_oblig node, Object data) { // a <-{min} b or a <-{minl2r} b (equivalent) // With optional context(s) // a <-{min} b / left _ right // a <-{min} b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-{min} b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-{min} X / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OBLIG); rsynpt.setMatchType(RuleMatchType.MIN_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } public Object visit(ASTrule_left_arrow_min_l2r_opt node, Object data) { // a <-? {min} b or a <-{minl2r} b (equivalent) // With optional context(s) // a <-? {min} b / left _ right // a <-? {min} b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-? {min} b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-? {min} X / ... int daughterCount = node.jjtGetNumChildren(); // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data); // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts) stack.pop(); // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT); rsynpt.setObligType(RuleObligType.OPT); rsynpt.setMatchType(RuleMatchType.MIN_L2R); // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i); node.jjtGetChild(i).jjtAccept(this, data); if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>) stack.pop()); } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data); // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent(); if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts); } else { stack.push(compileRuleSemanticParts(semanticParts)); } return data; } /* public Object visit(ASTrule_left_arrow_min_r2l_oblig node, Object data) { // a <-{minr2l} b // With optional context(s) // a <-{minr2l} b / left _ right // a <-{minr2l} b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-{minr2l} b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-{minr2l} X / ... int daughterCount = node.jjtGetNumChildren() ; // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data) ; // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts)stack.pop() ; // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT) ; rsynpt.setObligType(RuleObligType.OBLIG) ; rsynpt.setMatchType(RuleMatchType.MIN_R2L) ; // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i) ; node.jjtGetChild(i).jjtAccept(this, data) ; if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>)stack.pop()) ; } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>)stack.pop()) ; } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data) ; // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent() ; if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts) ; } else { stack.push(compileRuleSemanticParts(semanticParts)) ; } return data ; } */ /* public Object visit(ASTrule_left_arrow_min_r2l_opt node, Object data) { // a <-? {minr2l} b // With optional context(s) // a <-? {minr2l} b / left _ right // a <-? {minr2l} b / l1 _ r1 || l2 _ r2 ... (one rule with mult. contexts) // With optional where clause(s) // a <-? {minr2l} b / l _ r { where ...} (there can be multiple where clauses) // // Can also be a Markup Rule Y ... Z <-? {minr2l} X / ... int daughterCount = node.jjtGetNumChildren() ; // if 1, then rule_lhs only // if 2, then either // rule_lhs and rule_rhs // or rule_lhs and where_clauses // if 3, then rule_lhs, rule_rhs, where_clauses // There will always be an ASTrule_lhs as the 0th child, // with upper and lower ASTs; evaluate it node.jjtGetChild(0).jjtAccept(this, data) ; // leaves a new RuleSyntacticParts object on the stack RuleSyntacticParts rsynpt = (RuleSyntacticParts)stack.pop() ; // set what we know from the rule arrow rsynpt.setArrowType(RuleArrowType.LEFT) ; rsynpt.setObligType(RuleObligType.OPT) ; rsynpt.setMatchType(RuleMatchType.MIN_R2L) ; // Collect the parts from the rest of the AST, if any, starting // at Child(1). // There might be an ASTrule_rhs and/or an ASTwhere_clauses for (int i = 1; i < daughterCount; i++) { Object obj = node.jjtGetChild(i) ; node.jjtGetChild(i).jjtAccept(this, data) ; if (obj instanceof ASTrule_rhs) { // then there's an ArrayList<RuleContextSyntacticParts> on the stack rsynpt.setContexts((ArrayList<RuleContextSyntacticParts>)stack.pop()) ; } else { // it must be an ASTwhere_clauses // then there's an ArrayList<ArrayList<RuleLocalVarSetting>> on // the stack rsynpt.setLocalVarSettings((ArrayList<ArrayList<RuleLocalVarSetting>>)stack.pop()) ; } } // the big advantage of the RuleSyntacticParts is that it organizes the settings // for the where-clauses // now convert the one RuleSemanticParts object into a list of RuleSemanticParts // objects. Can be one to many because of where-clauses and/or epenthesis. ArrayList<RuleSemanticParts> semanticParts = compileRuleSyntacticParts(rsynpt, (InterpData) data) ; // If the mother node is ASTnet_parallel_func_call, // just push the semanticParts. // but if not, then convert the semanticParts into an Fst and push the Fst Object mother = node.jjtGetParent() ; if (mother instanceof ASTnet_parallel_func_call) { stack.push(semanticParts) ; } else { stack.push(compileRuleSemanticParts(semanticParts)) ; } return data ; } */ public Object visit(ASTrestriction_exp node, Object data) { // 2 daughters: restriction_lhs // which should have one regexp daughter // e.g. lit_char or concatenated_exp // restriction_rhs // e.g. abc => left _ right // e.g. abc => foo _ bar || fum _ fang // Evaluate the 0th child, the LHS of the restriction // expression, e.g. the b in b => l _ r // For now, just get the straightforward Fst node.jjtGetChild(0).jjtAccept(this, data); Fst lhs = (Fst) stack.pop(); // Now evaluate the RHS of the restriction expression // (of type ASTrestriction_rhs) which can contain multiple // contexts node.jjtGetChild(1).jjtAccept(this, data); // Hulden: // for one context L _ R, where x is a special restriction delimiter // [\x* [L x \x* x R] \x* ] // for two contexts, L1 _ R1 || L2 _ R2 // [\x* [ [L1 x \x* x R1] | [L2 x \x* x R2] ] \x*] // etc. Fst rhs = (Fst) stack.pop(); Fst resultFst = interpRestrictionExp(lhs, rhs, false); // false here means that this restriction is NOT part of the compilation // of an alternation rule. Rather it is for a stand-alone // restriction expression like A => L _ R stack.push(resultFst); return data; } public Object visit(ASTrestriction_lhs node, Object data) { // should have one daughter, some kind of regular expression // just evaluate it and leave the Fst object on the stack node.jjtGetChild(0).jjtAccept(this, data); return data; } // for => restrictions that compile into acceptors public Object visit(ASTrestriction_rhs node, Object data) { // Syntax: RHS of a => left _ right // a => left1 _ right1 || left2 _ right2 ... // will contain one or more ASTrestriction_context node daughters // Hulden // each individual context L _ R will be interpreted first as [L x \x* x R] // where x is a special restriction-delimiter symbol (here **RD). // These get unioned together, and then the result is surrounded // with \x*, i.e. the whole rhs is // [ \x* unionOfContexts \x* ] // Interpret all the daughters, each one interprets as an // Fst that is unioned into Fst unionOfContexts = lib.EmptyLanguageFst(); int daughterCount = node.jjtGetNumChildren(); // loop through the daughters, one for each context; // each daughter is of type ASTrestriction_context for (int i = 0; i < daughterCount; i++) { node.jjtGetChild(i).jjtAccept(this, data); Fst daughterFst = (Fst) stack.pop(); unionOfContexts = lib.UnionIntoFirstInPlace(unionOfContexts, daughterFst); } // Hulden: [ \x* unionOfContext \x* ] for whole RHS Fst resultFst = lib.Concat3Fsts(hulden.NotRestDelimStarFst(), unionOfContexts, hulden.NotRestDelimStarFst()); stack.push(resultFst); return data; } // for alternation rules public Object visit(ASTrule_rhs node, Object data) { // a rule_rhs will contain one or more 'context' daughters // each of which is either ASTone_level_rule_context or // ASTtwo_level_rule_context // result ArrayList<RuleContextSyntacticParts> contexts = new ArrayList<RuleContextSyntacticParts>(); for (int i = 0; i < node.jjtGetNumChildren(); i++) { // each context can be either ASTone_level_rule_context or // ASTtwo_level_rule_context // both, when evaluated, push a RuleContextSyntacticParts object node.jjtGetChild(i).jjtAccept(this, data); RuleContextSyntacticParts rcsp = (RuleContextSyntacticParts) stack.pop(); contexts.add(rcsp); } stack.push(contexts); return data; } // for a rule context with the _ operator public Object visit(ASTone_level_rule_context node, Object data) { RuleContextSyntacticParts rcsp = new RuleContextSyntacticParts(RuleContextLevels.ONE); // a RuleContextSyntacticParts stores the two context sides as ASTs // the constructor initially sets both contexts to null // could have ASTleft_rule_context or ASTright_rule_context, // both or neither; for right now, collect the ASTs; don't evaluate them for (int j = 0; j < node.jjtGetNumChildren(); j++) { Object contextSide = node.jjtGetChild(j); if (contextSide instanceof ASTleft_rule_context) { rcsp.setLeftRuleContext((ASTleft_rule_context) contextSide); } else { rcsp.setRightRuleContext((ASTright_rule_context) contextSide); } } stack.push(rcsp); return data; } // for a rule context with the 2_2 (or equivalent) operator public Object visit(ASTtwo_level_rule_context node, Object data) { RuleContextSyntacticParts rcsp = new RuleContextSyntacticParts(RuleContextLevels.TWO); // could have ASTleft_rule_context or ASTright_rule_context, // both or neither; for right now, collect the ASTs; don't evaluate them for (int j = 0; j < node.jjtGetNumChildren(); j++) { Object contextSide = node.jjtGetChild(j); if (contextSide instanceof ASTleft_rule_context) { rcsp.setLeftRuleContext((ASTleft_rule_context) contextSide); } else { rcsp.setRightRuleContext((ASTright_rule_context) contextSide); } } stack.push(rcsp); return data; } // for rules that compile into transducers public Object visit(ASTleft_rule_context node, Object data) { // should have one daughter, some kind of regexp node.jjtGetChild(0).jjtAccept(this, data); // leave the Fst on the stack return data; } // for rules that compile into transducers public Object visit(ASTright_rule_context node, Object data) { // should have one daughter, some kind of regexp node.jjtGetChild(0).jjtAccept(this, data); // leave the Fst on the stack return data; } // for => restrictions that compile into acceptors public Object visit(ASTrestriction_context node, Object data) { // Can have // 1. left_restriction_context and right_restriction_context // 2. just a left_restriction_context // 3. just a right_restriction_context // 4. no daughters at all int daughterCount = node.jjtGetNumChildren(); // get the values for the two contexts (just straightforward // FSTs for now) Fst leftContext; Fst rightContext; if (daughterCount == 2) { // there is both a leftContext and a rightContext node.jjtGetChild(0).jjtAccept(this, data); leftContext = (Fst) stack.pop(); node.jjtGetChild(1).jjtAccept(this, data); rightContext = (Fst) stack.pop(); } else if (daughterCount == 1) { // just a rightContext, or a leftContext // (the missing context is just the empty string language node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetChild(0) instanceof ASTleft_restriction_context) { leftContext = (Fst) stack.pop(); rightContext = lib.EmptyStringLanguageFst(); } else { leftContext = lib.EmptyStringLanguageFst(); rightContext = (Fst) stack.pop(); } } else { // no left or right context leftContext = lib.EmptyStringLanguageFst(); rightContext = lib.EmptyStringLanguageFst(); } Fst resultFst = interpRestrictionContext(leftContext, rightContext); stack.push(resultFst); return data; } // for => restrictions that compile into acceptors public Object visit(ASTleft_restriction_context node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); // just leave the resulting Fst on the stack return data; } // for => restrictions that compile into acceptors public Object visit(ASTright_restriction_context node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); // just leave the resulting Fst on the stack return data; } private void cartProdWhereClauses(ArrayList<ArrayList<ArrayList<RuleLocalVarSetting>>> listOfListOfList, ArrayList<RuleLocalVarSetting> current, int k, ArrayList<ArrayList<RuleLocalVarSetting>> result) { if (k == listOfListOfList.size()) { result.add(current); } else { for (int j = 0; j < listOfListOfList.get(k).size(); j++) { ArrayList<RuleLocalVarSetting> next = new ArrayList<RuleLocalVarSetting>(current); next.addAll(listOfListOfList.get(k).get(j)); cartProdWhereClauses(listOfListOfList, next, k + 1, result); } } } public Object visit(ASTwhere_clauses node, Object data) { // will have one or more where-clause daughters, either // ASTwhere_matched_clause or // ASTwhere_mixed_clause // each one when evaluated will push onto the stack an object of type // ArrayList<ArrayList<RuleLocalVarSetting>> // // Where there are multiple where-clause daughters, need to computer the // Cartesian Product of all the varName-Fst settings ArrayList<ArrayList<ArrayList<RuleLocalVarSetting>>> listOfListOfList = new ArrayList<ArrayList<ArrayList<RuleLocalVarSetting>>>(); // evaluate each of the individual where clauses for (int i = 0; i < node.jjtGetNumChildren(); i++) { node.jjtGetChild(i).jjtAccept(this, data); // each leaves an ArrayList<ArrayList<RuleLocalVarSetting>> object on the stack // add to the list of list of list listOfListOfList.add((ArrayList<ArrayList<RuleLocalVarSetting>>) stack.pop()); } // the goal of the interpretation of the ASTwhere_clauses is to return // one ArrayList<ArrayList<RuleLocalVarSetting>>> ArrayList<ArrayList<RuleLocalVarSetting>> result = null; // Each ArrayList<RuleLocalVarSetting> is a set of local var // settings such as $a = b, $b = p under which the _syntactic_ rule parts // will be compiled to create a SemanticRuleParts object (and if that // SemanticRuleParts object has an input side that matches the empty string, // and if that input side matches more than just the empty string, then // a second SemanticRuleParts object will result to handle the epenthesis case.;:w if (listOfListOfList.size() == 1) { // just one where clause, probably the typical case result = listOfListOfList.get(0); } else { // multiple where clauses under ASTwhere_clauses // compute the Cartesian Product of the daughter values, // each of which is an ArrayList<ArrayList<RuleLocalVarSetting>> // to get (in result) a single // ArrayList<ArrayList<RuleLocalVarSetting>> result = new ArrayList<ArrayList<RuleLocalVarSetting>>(); ArrayList<RuleLocalVarSetting> current = new ArrayList<RuleLocalVarSetting>(); cartProdWhereClauses(listOfListOfList, current, 0, result); } // KRB: See if this works as a test. /* for (Iterator<ArrayList<RuleLocalVarSetting>> lli = result.iterator(); lli.hasNext(); ) { ArrayList<RuleLocalVarSetting> listOfRuleLocalVarSetting = lli.next() ; System.out.print("[ ") ; for (int l = 0; l < listOfRuleLocalVarSetting.size(); l++) { System.out.print("[ ") ; System.out.print(listOfRuleLocalVarSetting.get(l).getVarName() + " ") ; System.out.print(ObjectUtils.identityToString(listOfRuleLocalVarSetting.get(l).getFstValue()) + " ]") ; } System.out.println("]") ; } */ stack.push(result); return data; } public Object visit(ASTwhere_matched_clause node, Object data) { // Syntax, e.g. // { where $a _E_ $@(b, d, g), $b _E_ $@(p, t, k) } // or // where_matched { $a _E_ $@(b, d, g), $b _E_ $@(p, t, k) } // There will be one or more local variables like $a and $b here. // Each of the net lists must have the same non-zero number of elements. // // ASTwhere_matched_clause // ASTelmt_of_net_list_exp 1 or more of these daughters // ASTnet_id // ASTnet_list_exp // // Result: // List // List $a = b $b = p (each a RuleLocalVarSetting) // List $a = d $b = t // List $a = g $b = k int daughterCount = node.jjtGetNumChildren(); // to collect the local variable names as strings (do not evaluate them) ArrayList<String> varNames = new ArrayList<String>(); // to collect the netlists of Fst values ArrayList<ArrayList<Fst>> listOfListOfFst = new ArrayList<ArrayList<Fst>>(); // collect the variable names and lists of Fsts // make sure that the Fst lists are of equal size/length (required for where_matched) int listSize = 0; for (int i = 0; i < daughterCount; i++) { ASTelmt_of_net_list_exp elmtOfNetListExp = (ASTelmt_of_net_list_exp) node.jjtGetChild(i); // don't eval the ASTnet_id, just get the String image varNames.add(((ASTnet_id) (elmtOfNetListExp.jjtGetChild(0))).getImage()); elmtOfNetListExp.jjtGetChild(1).jjtAccept(this, data); // leaves a NetList object on the stack NetList netList = (NetList) stack.pop(); if (i == 0) { // first Fst list found, set the size; all the remaining // Fst lists must have the same size listSize = netList.size(); } else { if (netList.size() != listSize) { throw new WhereClauseException( "The Fst lists in a where_matched clause must all have the same size. Zeroth list has size: " + listSize + ". " + i + "th list has size: " + netList.size() + "."); } } listOfListOfFst.add(netList.getArrayList()); } ArrayList<ArrayList<RuleLocalVarSetting>> result = new ArrayList<ArrayList<RuleLocalVarSetting>>(); // j indexes over the Fst values in all the Fst lists for (int j = 0; j < listSize; j++) { ArrayList<RuleLocalVarSetting> listOfSetting = new ArrayList<RuleLocalVarSetting>(); // index over the daughters of ASTwhere_matched_clause for (int i = 0; i < daughterCount; i++) { listOfSetting.add(new RuleLocalVarSetting(varNames.get(i), listOfListOfFst.get(i).get(j))); } result.add(listOfSetting); } stack.push(result); return data; } private void cartProdWhereMixed(ArrayList<String> listOfVarNames, ArrayList<ArrayList<Fst>> listOfListOfFst, ArrayList<RuleLocalVarSetting> current, int k, ArrayList<ArrayList<RuleLocalVarSetting>> result) { if (k == listOfListOfFst.size()) { result.add(new ArrayList<RuleLocalVarSetting>(current)); } else { for (int j = 0; j < listOfListOfFst.get(k).size(); j++) { current.set(k, new RuleLocalVarSetting(listOfVarNames.get(k), listOfListOfFst.get(k).get(j))); cartProdWhereMixed(listOfVarNames, listOfListOfFst, current, k + 1, result); } } } public Object visit(ASTwhere_mixed_clause node, Object data) { // ASTwhere_mixed_clause // ASTelmt_of_net_list_exp 1 or more of these // ASTnet_id just get the String image // ASTnet_list_exp could be a variable, or literal $@(a, b, c) // Evaluate it! get the Fsts in the current Frame ArrayList<String> listOfVarNames = new ArrayList<String>(); ArrayList<ArrayList<Fst>> listOfListOfFst = new ArrayList<ArrayList<Fst>>(); int daughterCount = node.jjtGetNumChildren(); // collect the varNames and the lists of Fst for (int i = 0; i < daughterCount; i++) { ASTelmt_of_net_list_exp elmtOfNetListExp = (ASTelmt_of_net_list_exp) node.jjtGetChild(i); // always has two daughters: ASTnet_id and ASTnet_list_exp // don't evaluate the net_id, just get the String image String varName = ((ASTnet_id) elmtOfNetListExp.jjtGetChild(0)).getImage(); listOfVarNames.add(varName); // evaluate daughter 1, the ASTnet_list_exp elmtOfNetListExp.jjtGetChild(1).jjtAccept(this, data); // leaves a NetList object on the stack // a NetList has method .getLinkedList() that returns LinkedList<Fst> // and .getArrayList() that returns an ArrayList<Fst> NetList netList = (NetList) stack.pop(); listOfListOfFst.add(netList.getArrayList()); } ArrayList<ArrayList<RuleLocalVarSetting>> result = new ArrayList<ArrayList<RuleLocalVarSetting>>(); ArrayList<RuleLocalVarSetting> current = new ArrayList<RuleLocalVarSetting>(); // need to 'populate' current so that the indexed slots can be re-assigned for (int i = 0; i < daughterCount; i++) { current.add(new RuleLocalVarSetting("", null)); } // now compute the Cartesian Product of var-Fst settings cartProdWhereMixed(listOfVarNames, listOfListOfFst, current, 0, result); stack.push(result); return data; } public Object visit(ASTelmt_of_net_list_exp node, Object data) { // KRB: shouldn't be interpreted directly System.out.println("Interp: ASTelmt_of_net_list_exp node not implemented."); return data; } public Object visit(ASTdifference_exp node, Object data) { // N.B. "difference" here means Fst difference; // "subtraction" is the term used herein for the arithmetic operation // there should be exactly two daughters, syntactically constrained node.childrenAccept(this, data); // the second arg will be on top of the stack Fst secondFst = (Fst) stack.pop(); Fst firstFst = (Fst) stack.pop(); Fst resultFst = lib.Difference(firstFst, secondFst); stack.push(resultFst); return data; } public Object visit(ASTunioned_exp node, Object data) { // The args to be unioned are parsed into a flat AST // (union is treated like an n-ary operation) int last = node.jjtGetNumChildren() - 1; // start with a new empty FST, union all the argument Fsts into it // use EmptyLanguageWithStartStateFst instead of EmptyLanguageFst, // this one creates a one-state Fst (start state, not final), // which gives expected results of something like a|b when // optimization is turned off Fst resultFst = lib.EmptyLanguageWithStartStateFst(); // some unions, especially in lexicon-like right-recursive // phrase-structure grammars, can get very long, // so optimize only at intervals; KRB: magic number int optimizeInterval = 1000; boolean optimize; for (int i = 0; i <= last; i++) { node.jjtGetChild(i).jjtAccept(this, data); Fst daughterFst = (Fst) stack.pop(); if ((i == last) || ((i % optimizeInterval) == 0)) { optimize = true; } else { optimize = false; } resultFst = lib.UnionIntoFirstInPlace(resultFst, daughterFst, optimize); } stack.push(resultFst); return data; } public Object visit(ASTintersected_exp node, Object data) { // KRB: compare to method for ASTcomposed_exp // A & B & C ... is parsed into a flat AST // (treated like an n-ary operation) // there will always be at least two daughters // (this is syntactically constrained) // get a new sigma* Java Fst (hasOther will be set to true) Fst resultFst = lib.UniversalLanguageFst(); // see Java func above int daughterCount = node.jjtGetNumChildren(); // Loop through the daughters, intersecting each one into the result for (int i = 0; i < daughterCount; i++) { node.jjtGetChild(i).jjtAccept(this, data); Fst daughterFst = (Fst) stack.pop(); resultFst = lib.Intersect(resultFst, daughterFst); } stack.push(resultFst); return data; } public Object visit(ASTconcatenation_exp node, Object data) { // Concatenation is parsed/evaluated as an n-ary operation int last = node.jjtGetNumChildren() - 1; // start with an empty-string-language Fst // concatenate the daughter Fsts into it Fst resultFst = lib.EmptyStringLanguageFst(); boolean optimize; for (int i = 0; i <= last; i++) { node.jjtGetChild(i).jjtAccept(this, data); Fst daughterFst = (Fst) stack.pop(); optimize = (i == last) ? true : false; resultFst = lib.Concat(resultFst, daughterFst, optimize); } stack.push(resultFst); return data; } public Object visit(ASTcrossproduct_exp node, Object data) { // there should be exactly two daughters, syntactically // constrained node.childrenAccept(this, data); Fst secondFst = (Fst) stack.pop(); Fst firstFst = (Fst) stack.pop(); Fst resultFst = lib.Crossproduct(firstFst, secondFst); stack.push(resultFst); return data; } public Object visit(ASTweight_exp node, Object data) { // KRB: just float (Double) values for now (Tropical semiring) // user might use just an int (Long), so convert as necessary node.jjtGetChild(0).jjtAccept(this, data); // semiring generalization point float weight; Object obj = stack.pop(); if (obj instanceof Double) { weight = ((Double) obj).floatValue(); } else { weight = ((Long) obj).floatValue(); } // Create a two-state, one arc Fst with eps:eps label and // indicated arc weight, // and Tropical weight neutral 0.0 final weight stack.push(lib.OneArcFst(lib.Epsilon, lib.Epsilon, weight, (float) 0.0)); return data; } public Object visit(ASTcomplement_exp node, Object data) { // syntax is ~A where A is any regular expression // calculated as (.* - A) // there should be just one daughter, an ASTregexp node.jjtGetChild(0).jjtAccept(this, data); Fst positiveFst = (Fst) stack.pop(); Fst resultFst = lib.Complement(positiveFst); // the result should never match the # used in rules KRB ruleany // Rethink: 2015-01-18 the other in a _rule_ FST should not match # //resultFst.getSigma().add(symmap.putsym(hulden.ruleWordBoundarySym)) ; stack.push(resultFst); return data; } public Object visit(ASTkleene_star node, Object data) { // Syntax: x* // should be just one daughter node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) stack.pop(); Fst resultFst = lib.KleeneStar(fst); stack.push(resultFst); return data; } public Object visit(ASTkleene_plus node, Object data) { // Syntax: x+ // should be just one daughter node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) stack.pop(); Fst resultFst = lib.KleenePlus(fst); stack.push(resultFst); return data; } public Object visit(ASToptional node, Object data) { // just one argument, syntactically constrained node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst on stack Fst fst = (Fst) stack.pop(); Fst resultFst = lib.UnionIntoFirstInPlace(lib.EmptyStringLanguageFst(), fst); stack.push(resultFst); return data; } public Object visit(ASTiterated_exp node, Object data) { // two daughters: zeroth is a regexp // first is either ASTiteration_exact syntax was {4} // or ASTiteration_low syntax was {2,} // or ASTiteration_low_high syntax was {2,4} // or ASTiteration_high syntax was {,4} // equiv. to {0,4} node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst on the stack Fst fst = (Fst) stack.pop(); Fst resultFst; node.jjtGetChild(1).jjtAccept(this, data); // leaves one or two Longs on the stack // check the type of Child(1) to see if one or two Longs // remain on the stack, and how to treat them. Object obj = node.jjtGetChild(1); if (obj instanceof ASTiteration_low_high) { // there will be two Long values on the stack. // the high value will be on top of the stack long high = ((Long) stack.pop()).longValue(); long low = ((Long) stack.pop()).longValue(); resultFst = lib.Iterate(fst, low, high); } else if (obj instanceof ASTiteration_low) { // just one Long on the stack long low = ((Long) stack.pop()).longValue(); // use -1 "high" arg to indicate unlimited resultFst = lib.Iterate(fst, low, -1L); } else if (obj instanceof ASTiteration_high) { // just one Long on the stack long high = ((Long) stack.pop()).longValue(); // the low value is 0 resultFst = lib.Iterate(fst, 0L, high); } else { // it's ASTiteration_exact, just one Long on the stack long exact = ((Long) stack.pop()).longValue(); resultFst = lib.Iterate(fst, exact, exact); } stack.push(resultFst); return data; } public Object visit(ASTiteration_low_high node, Object data) { node.childrenAccept(this, data); // should usually leave two Long objects on the stack (but might be Double) // syntactically, you could have complicated arithmetic expressions. // Leave just Long objects on the stack (in the right order). // // the object left on top of the stack holds the high value Object high = stack.pop(); Object low = stack.pop(); if (low instanceof Long) { stack.push(low); } else { stack.push(new Long(((Double) low).longValue())); } if (high instanceof Long) { stack.push(high); } else { stack.push(new Long(((Double) high).longValue())); } // leaves the high Long on top of the stack, // with the low Long just under it return data; } public Object visit(ASTiteration_low node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); // object on the Stack usually a Long, but could be a Double Object low = stack.pop(); if (low instanceof Long) { stack.push(low); } else { stack.push(new Long(((Double) low).longValue())); } return data; } public Object visit(ASTiteration_exact node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); Object exact = stack.pop(); if (exact instanceof Long) { stack.push(exact); } else { stack.push(new Long(((Double) exact).longValue())); } return data; } public Object visit(ASTiteration_high node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); Object high = stack.pop(); if (high instanceof Long) { stack.push(high); } else { stack.push(new Long(((Double) high).longValue())); } return data; } public Object visit(ASTlit_char node, Object data) { String image = node.getImage(); // KRB: possible normalization point // see SymMap.java int cpv = symmap.putsym(image); // create a two-state, one arc Fst with cpv:cpv label // semiring generalization point stack.push(lib.OneArcFst(cpv)); return data; } public Object visit(ASTmultichar_symbol node, Object data) { String image = node.getImage(); if (image.startsWith("__")) { throw new KleeneInterpreterException( "Multicharacter symbols starting with __ (two underscores) are reserved for internal system use."); } else if (image.startsWith("**")) { throw new KleeneInterpreterException( "Multicharacter symbols starting with ** (two asterisks) are reserved for internal system use."); } else if (image.equals("OTHER_ID")) { throw new KleeneInterpreterException( "The multicharacter symbol OTHER_ID is reserved for internal system use."); } else if (image.equals("OTHER_NONID")) { throw new KleeneInterpreterException( "The multicharacter symbol OTHER_NONID is reserved for internal system use."); } else if (image.equals("KLEENE**@#@")) { // tokenizer finds unliteralized #, in a rule/restriction // context, and resets the image to "KLEENE**@#@" // change it here to **@#@ image = "**@#@"; } int cpv = symmap.putsym(image); // create a two-state, one arc Fst with cpv:cpv label // semiring generalization point stack.push(lib.OneArcFst(cpv)); return data; } public Object visit(ASTsquare_bracket_multichar_symbol node, Object data) { String image = node.getImage(); if (image.startsWith("__")) { throw new KleeneInterpreterException( "Multicharacter symbols starting with __ (two underscores) are reserved for internal system use."); } if (image.startsWith("**")) { throw new KleeneInterpreterException( "Multicharacter symbols starting with ** (two asterisks) are reserved for internal system use."); } int cpv = symmap.putsym(image); // create a two-state, one arc Fst with cpv:cpv label stack.push(lib.OneArcFst(cpv)); return data; } public Object visit(ASTdouble_quoted_string node, Object data) { // There can be any number of characters (including zero) in the string int last = node.jjtGetNumChildren() - 1; // The daughters are all of type ASTdouble_quoted_char Fst resultFst = lib.EmptyStringLanguageFst(); boolean optimize; // optimize only on the last loop for (int i = 0; i <= last; i++) { node.jjtGetChild(i).jjtAccept(this, data); // OpenFst's Concat(first, second) is destructive of the first arg Fst daughterFst = (Fst) stack.pop(); optimize = (i == last) ? true : false; resultFst = lib.Concat(resultFst, daughterFst, optimize); } stack.push(resultFst); return data; } public Object visit(ASTdouble_quoted_char node, Object data) { // A double_quoted_char is a character inside double quotes String image = node.getImage(); // KRB: possible Unicode normalization point // KRB: ignore, for initial testing, the possibility of // double_quoted_char matching a letter plus following combining diacritics; // take care of this when dealing with robust normalization; see // SymMap.java; cf to ASTlit_char int cpv = symmap.putsym(image); // get a two-state, one arc Fst with cpv:cpv label // no need to optimize, I think stack.push(lib.OneArcFst(cpv)); return data; } public Object visit(ASTchar_union node, Object data) { // Syntax is [aeiou], [A-Za-z0-9], [abcm-z], etc. int last = node.jjtGetNumChildren() - 1; // use EmptyLanguageWithStartStateFst instead of EmptyLanguageFst, // the former creates a one-state network (start, not final), and // gives more intuitive results when setOptimize is set to false Fst resultFst = lib.EmptyLanguageWithStartStateFst(); // there are four possible types of daughter: // char_range, // square_bracket_char // square_bracket_lit_hyphen // square_bracket_multichar_symbol // (all result in an Fst pushed on the stack) boolean optimize; for (int i = 0; i <= last; i++) { node.jjtGetChild(i).jjtAccept(this, data); // OpenFst's Union(first, second) is destructive // of the first arg Fst daughterFst = (Fst) stack.pop(); optimize = (i == last) ? true : false; resultFst = lib.UnionIntoFirstInPlace(resultFst, daughterFst, optimize); } stack.push(resultFst); return data; } public Object visit(ASTcomplement_char_union node, Object data) { // Syntax: [^abc] [^a-z] etc. int last = node.jjtGetNumChildren() - 1; // Compute as . - [...] // First, compute the Fst covering the characters in [^...] // Cf ASTchar_union // start with an empty FST, union into it // use EmptyLanguageWithStartStateFst instead of EmptyLanguageFst, // the former creates a one-state (start, not final) fst and gives // more intuitive results when setOptimize is set to false Fst charUnionFst = lib.EmptyLanguageWithStartStateFst(); boolean optimize; for (int i = 0; i <= last; i++) { node.jjtGetChild(i).jjtAccept(this, data); // will leave an Fst object on the stack Fst daughterFst = (Fst) stack.pop(); optimize = (i == last) ? true : false; charUnionFst = lib.UnionIntoFirstInPlace(charUnionFst, daughterFst, optimize); } // Second, get a network for ., subtract charUnionFst Fst resultFst = lib.Difference(lib.SigmaFst(), charUnionFst); // the result should never match the # used in rules KRB ruleany // Rethink: 2015-01-18 the OTHER in a _rule_ FST should never match # //resultFst.getSigma().add(symmap.putsym(hulden.ruleWordBoundarySym)) ; stack.push(resultFst); return data; } public Object visit(ASTchar_range node, Object data) { // Syntax: a-z inside [...] or [^...] // just a shorthand for a symbol union // always two daughters, always ASTlit_char? String imageFirst = ((ASTlit_char) node.jjtGetChild(0)).getImage(); String imageLast = ((ASTlit_char) node.jjtGetChild(1)).getImage(); // KRB: ignore, for initial testing, the possibility of // lit_char matching a letter plus following combining diacritics; // take care of this when dealing with robust normalization; see // SymMap.java int cpvFirst = symmap.putsym(imageFirst); int cpvLast = symmap.putsym(imageLast); if (cpvFirst > cpvLast) { // then the range is impossible/badly formed throw new CharRangeException( "In a character range [x-y], the code point value of y must be greater or equal to the code point value of x."); } Fst resultFst = lib.CharRangeUnionFst(cpvFirst, cpvLast); // need to put the whole range of chars in the symmap // for proper 'dot' display of labels in networks; and // add them to the sigma for (int cpv = cpvFirst; cpv <= cpvLast; cpv++) { symmap.putsym(lib.stringFromCpv(cpv)); resultFst.getSigma().add(cpv); // no need to worry about OTHER } stack.push(resultFst); return data; } public Object visit(ASTnet_func_call node, Object data) { // Usual Syntax: $^myunion(a, b) has an Fst value // e.g. $^myunion($a, $b) $^myfunc($a, $b, $c=abc, $d=def) or // $^myfunc($a = a*b+, $b = [a-m]+) // // net_func_call always has two daughters: // // net_func_call // net_func_exp // arg_list // // N.B. net_func_exp could be // net_func_id e.g. $^myfunc OR // $^ exp e.g. $^(...){...} OR // net_func_func_call e.g. $^^func(...) // so net_func_exp needs to be evaluated // (will leave a FuncValue object on the stack) // Evaluate daughter 0, the net_func_exp node.jjtGetChild(0).jjtAccept(this, data); // Should leave a FuncValue on the stack FuncValue funcValue = (FuncValue) stack.pop(); // now evaluate the arg_list, which can have 0 to 2 daughters // If either daughter is present, it is non-empty. // If both daughters are present, // positional_args is always before named_args // arg_list // positional_args // named_args // // N.B. the arguments have to be evaluated in the current Frame, // before allocating a new daughter frame for the execution of the // function body. If either daughter is present, it is non-empty. // Evaluate daughter 1, the arg_list node.jjtGetChild(1).jjtAccept(this, data); // should leave on the stack, from top down // 1. an ArgCounts object (containing positional_args_count // and named_args_count) // 2. the positional arguments (in syntactic order), // the number being positional_args_count // 3. the named arguments (each represented by a NamedArg object), // the number being named_args_count // // first pop off the ArgCounts object, // leaving the evaluated args (if any) on the stack ArgCounts ac = (ArgCounts) stack.pop(); // Now allocate a new Frame for execution of this function call // (N.B. released below when the function returns) env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the arg values, in the new Frame try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all Exceptions and release the frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block); more precisely, // send a message to the function block telling it to accept this // IntepreterVisitor try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the Frame created for the execution of this func call env.releaseFrame(); // Check return value; // there should be an Fst object left on the stack // (this is ASTnet_func_call) by a return stmt in the funcBlock. // Make sure that the object left on the stack // is a Java Fst object. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("Net valued function call failed to return a net."); } else if (!(obj instanceof Fst)) { throw new FuncCallException("Net valued function call returns incorrect type."); } return data; } public Object visit(ASTvoid_func_call node, Object data) { // e.g. ^myfoo($a, $b) // ^myfunc($a, $b, $c=abc, $d=def) or // ^myfunc($a = a*b+, $b = [a-m]+) // // void_func_call // void_func_exp // arg_list // // N.B. void_func_exp could be // void_func_id or // ^ exp // void_func_func_call // so it needs to be evaluated node.jjtGetChild(0).jjtAccept(this, data); // Should leave a FuncValue on the stack FuncValue funcValue = (FuncValue) stack.pop(); // now evaluate the arg_list, which can have up to two daughters // arg_list // positional_args // named_args // // N.B. that the arguments have to be evaluated in the current Frame, // before allocating a new daughter frame for the execution of the // function body. If either daughter is present, it is non-empty. node.jjtGetChild(1).jjtAccept(this, data); // should leave on the stack, from top down // 1. an ArgCounts object // 2. the positional arguments (in syntactic order) // 3. the named arguments (each represented by a NamedArg object) // // first pop off the ArgCounts object, leaving the evaluated args ArgCounts ac = (ArgCounts) stack.pop(); // Now allocate a new Frame for the execution of this function call // (N.B. released below when the function returns) env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the arg values, in the new Frame try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all Exceptions and release the frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // a void function should return no real value; // push a special VoidValue object here; it should be on top of the // stack when the function returns (else there was some error) stack.push(new VoidValue()); // now execute the body of the function (a func_block) try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the Frame created for the execution of this func call env.releaseFrame(); // Check return value; there should be a VoidValue object left on the stack Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("Void function call failed to return properly."); } else if (!(obj instanceof VoidValue)) { throw new FuncCallException("Net valued function call returns incorrect type."); } stack.pop(); // get rid of the VoidValue object return data; } public Object visit(ASTnet_reverse_func_call node, Object data) { // just $^__reverse($arg) built-in, // wrapped as $^reverse($arg) // one daughter, syntactically constrained // ASTregexp node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) stack.pop(); // lib.Reverse() is not destructive. Fst resultFst = lib.Reverse(fst); stack.push(resultFst); return data; } public Object visit(ASTnet_shortestPath_func_call node, Object data) { // just $^__shortestPath($arg, #nshortest) built-in, // wrapped as $^shortestPath($arg, #nshortest=1) // two daughters: ASTregexp ASTnumexp (constrained by the parser) node.childrenAccept(this, data); // top Object on the stack should be a Long (perhaps Double) Object obj = stack.pop(); int nshortest; if (obj instanceof Long) { nshortest = ((Long) obj).intValue(); } else { nshortest = ((Double) obj).intValue(); } // next, an Fst object on the stack Fst fst = (Fst) stack.pop(); // OpenFst ShortestPath, returns a new Fst. if (nshortest < 0) { throw new KleeneArgException("The second arg to shortestPath must be >= 0"); } Fst resultFst = lib.ShortestPath(fst, nshortest); stack.push(resultFst); return data; } public Object visit(ASTnet_flatten_dest_func_call node, Object data) { // just $^__flatten!($fsm) // wrapped as $^flatten!($fsm) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // do not copy, just work on the fst in place int hardEpsilonSymVal = symmap.putsym(hulden.hardEpsilonSym); int otherIdSymVal = symmap.putsym(lib.otherIdSym); int otherNonIdSymVal = symmap.putsym(lib.otherNonIdSym); lib.FlattenInPlace(fst, hardEpsilonSymVal, otherIdSymVal, otherNonIdSymVal); correctSigmaOther(fst); stack.push(fst); return data; } public Object visit(ASTnet_flatten_func_call node, Object data) { // just $^__flatten($fsm) // wrapped as $^flatten($fsm) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getFromSymtab()) { // then need to work on a copy fst = lib.CopyFst(fst); } int hardEpsilonSymVal = symmap.putsym(hulden.hardEpsilonSym); int otherIdSymVal = symmap.putsym(lib.otherIdSym); int otherNonIdSymVal = symmap.putsym(lib.otherNonIdSym); lib.FlattenInPlace(fst, hardEpsilonSymVal, otherIdSymVal, otherNonIdSymVal); correctSigmaOther(fst); stack.push(fst); return data; } public Object visit(ASTnet_flatten4rule_dest_func_call node, Object data) { // just $^__flatten4rule!($fsm) // wrapped as $^flatten4rule!($fsm) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // do not copy, just work on the fst in place int hardEpsilonSymVal = symmap.putsym(hulden.hardEpsilonSym); //int otherIdSymVal = symmap.putsym(lib.otherIdSym) ; //int otherNonIdSymVal = symmap.putsym(lib.otherNonIdSym) ; //lib.Flatten4RuleInPlace(fst, hardEpsilonSymVal, otherIdSymVal, otherNonIdSymVal) ; lib.Flatten4RuleInPlace(fst, hardEpsilonSymVal); correctSigmaOther(fst); stack.push(fst); return data; } public Object visit(ASTnet_flatten4rule_func_call node, Object data) { // just $^__flatten4rule($fsm) // wrapped as $^flatten4rule($fsm) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getFromSymtab()) { // then need to work on a copy fst = lib.CopyFst(fst); } int hardEpsilonSymVal = symmap.putsym(hulden.hardEpsilonSym); //int otherIdSymVal = symmap.putsym(lib.otherIdSym) ; //int otherNonIdSymVal = symmap.putsym(lib.otherNonIdSym) ; //lib.Flatten4RuleInPlace(fst, hardEpsilonSymVal, otherIdSymVal, otherNonIdSymVal) ; lib.Flatten4RuleInPlace(fst, hardEpsilonSymVal); correctSigmaOther(fst); stack.push(fst); return data; } public Object visit(ASTnet_invert_func_call node, Object data) { // just $^__invert($arg) built-in, wrapped as $^invert($arg) // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getFromSymtab()) { // then need to work on a copy fst = lib.CopyFst(fst); } lib.InvertInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_invert_dest_func_call node, Object data) { // just $^__invert!($arg) built-in, wrapped as $^invert!($arg) // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // Don't make a copy // just invert the original network in place lib.InvertInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_optimize_func_call node, Object data) { // just $^__optimize($arg) built-in, wrapped as $^optimize($arg) // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); Fst resultFst; if (fst.getFromSymtab()) { // then need to work on a copy resultFst = lib.CopyFst(fst); } else { resultFst = fst; } // force the optimization lib.OptimizeInPlaceForce(resultFst); stack.push(resultFst); return data; } public Object visit(ASTnet_optimize_dest_func_call node, Object data) { // just $^__optimize!($arg) built-in, wrapped as $^optimize!($arg) // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // Don't make a copy // just optimize the original network in place // force the optimization lib.OptimizeInPlaceForce(fst); stack.push(fst); return data; } public Object visit(ASTnet_rmepsilon_func_call node, Object data) { // the function call is not destructive (i.e. returns a new net // if the argument is from a symbol table) // just $^__rmEpsilon($arg) built-in, wrapped as $^rmepsilon and // $^rmEpsilon // just one daughter: ASTregexp (constrained by the parser) // N.B. this Kleene function does not work in place for a network // that comes from the symbol table (and so has a name, or alias // names) bound to it node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getFromSymtab()) { // then need to work on and return a copy fst = lib.CopyFst(fst); } lib.RmEpsilonInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_rmepsilon_dest_func_call node, Object data) { // the function call is destructive (i.e. works in place) // just $^__rmEpsilon!($arg) built-in, wrapped as $^rmepsilon! and // $^rmEpsilon! // just one daughter: ASTregexp (constrained by the parser) // N.B. this Kleene function does not work in place for a network // that comes from the symbol table (and so has a name, or alias // names) bound to it node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); lib.RmEpsilonInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_determinize_func_call node, Object data) { // not destructive // just $^__determinize($arg) built-in, wrapped as $^determinize() // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getFromSymtab()) { // then need to work on and return a copy fst = lib.CopyFst(fst); } lib.DeterminizeInPlace(fst); // N.B. there is an lib.Determinze(fst) that is always // non-destructive, always returning a new Fst, but there's // no point in returning a new Fst if the input Fst is // not from the symbol table stack.push(fst); return data; } public Object visit(ASTnet_determinize_dest_func_call node, Object data) { // destructive // just $^__determinize!($arg) built-in, wrapped as $^determinize!() // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // Don't make a copy lib.DeterminizeInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_minimize_func_call node, Object data) { // this function call is not destructive, (i.e. returns a new net) // just $^__minimize($arg) built-in, wrapped as $^minimize() // just one daughter: ASTregexp (constrained by the parser) // N.B. the OpenFst Minimize() works in place (destructive), // so if the input comes from the symbol table (and so has // a name, or names, linked to it, then we have to work on // and return a copy node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getFromSymtab()) { // then need to work on a copy fst = lib.CopyFst(fst); } lib.MinimizeInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_minimize_dest_func_call node, Object data) { // destructive, (i.e. works in place) // just $^__minimize!($arg) built-in, wrapped as $^minimize!() // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // Don't make a copy lib.MinimizeInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_synchronize_func_call node, Object data) { // not destructive // just $^__synchronize($arg) built-in, wrapped as $^synchronize() // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getFromSymtab()) { // then need to work on and return a copy fst = lib.CopyFst(fst); } lib.SynchronizeInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_synchronize_dest_func_call node, Object data) { // destructive // just $^__synchronize!($arg) built-in, wrapped as $^synchronize!() // just one daughter: ASTregexp (constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // Don't make a copy lib.SynchronizeInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_diac_func_call node, Object data) { // built-in $^__diac(), wrapped various ways in predefined.kl // 3 args (syntactically constrained) // 0. destructive numexp() boolean // 1. fst regexp() // 2. projection regexp() should denote a language of one string // "input" (or "upper"), // "output" (or "lower"), // or "both" node.jjtGetChild(0).jjtAccept(this, data); boolean destructive = lib.isTrue(stack.pop()); // true for destructive node.jjtGetChild(1).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); node.jjtGetChild(2).jjtAccept(this, data); Fst projFst = (Fst) stack.pop(); String projString = lib.GetSingleString(projFst, "Last arg to convertCase must denote a language of exactly one string: input, upper, output, lower or both") .trim().toLowerCase(); boolean input = false; boolean output = false; if (projString.equals("input") || projString.equals("upper")) { input = true; } else if (projString.equals("output") || projString.equals("lower")) { output = true; } else if (projString.equals("both")) { input = true; output = true; } else { throw new FuncCallException("Last arg to convertCase must be: input, upper, output, lower or both"); } Fst workFst = fst; if (!destructive) { workFst = lib.CopyFst(fst); } lib.AddDiacInPlace(workFst, input, output); stack.push(workFst); return data; } public Object visit(ASTnet_case_func_call node, Object data) { // built-in $^__case(), wrapped various ways in predefined.kl // 7 args (syntactically constrained) // 0. destructive numexp() boolean // 1. convert numexp() boolean (true for convert, false for add/allow) // 2. all numexp() boolean (true for whole path, false for init) // 3. fst regexp() // 4. to_uc numexp() boolean // 5. to_lc numexp() boolean // 6. projection regexp() should denote a language of one string // "input" (or "upper"), // "output" (or "lower"), // or "both" node.jjtGetChild(0).jjtAccept(this, data); boolean destructive = lib.isTrue(stack.pop()); // true for destructive node.jjtGetChild(1).jjtAccept(this, data); boolean convert = lib.isTrue(stack.pop()); // true for convert, false for add/allow node.jjtGetChild(2).jjtAccept(this, data); boolean all = lib.isTrue(stack.pop()); // T means whole path, F means init only // ************** node.jjtGetChild(3).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); node.jjtGetChild(4).jjtAccept(this, data); boolean uc = lib.isTrue(stack.pop()); node.jjtGetChild(5).jjtAccept(this, data); boolean lc = lib.isTrue(stack.pop()); node.jjtGetChild(6).jjtAccept(this, data); Fst projFst = (Fst) stack.pop(); String projString = lib.GetSingleString(projFst, "Last arg to convertCase must denote a language of exactly one string: input, upper, output, lower, or both") .trim().toLowerCase(); boolean input = false; boolean output = false; if (projString.equals("input") || projString.equals("upper")) { input = true; } else if (projString.equals("output") || projString.equals("lower")) { output = true; } else if (projString.equals("both")) { input = true; output = true; } else { throw new FuncCallException("Last arg to convertCase must be: input, upper, output, lower or both"); } if (!destructive) { fst = lib.CopyFst(fst); } if (convert) { lib.ConvertCaseInPlace(fst, all, uc, lc, input, output); } else { // add case variants // the result should contain all the original arcs, // typically plus some new added arcs (a semantic union); lib.AddCaseInPlace(fst, all, uc, lc, input, output); } stack.push(fst); return data; } public Object visit(ASTnet_inputproj_dest_func_call node, Object data) { // syntax is the $^__inputproj!($arg) built-in // wrapped as $^inputproj!($arg) // one daughter, syntactically constrained // ASTregexp node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); lib.InputProjectionInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_inputproj_func_call node, Object data) { // syntax is the $^__inputproj($arg) built-in // one daughter, syntactically constrained // ASTregexp node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // not destructive Fst resultFst = lib.InputProjection(fst); stack.push(resultFst); return data; } public Object visit(ASTnet_outputproj_dest_func_call node, Object data) { // syntax is the $^__outputproj!($arg) built-in // wrapped as $^outputproj!($arg) // one daughter, syntactically constrained // ASTregexp node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); lib.OutputProjectionInPlace(fst); stack.push(fst); return data; } public Object visit(ASTnet_outputproj_func_call node, Object data) { // syntax is the $^__outputproj($arg) built-in // wrapped as $^outputproj($arg) // one daughter, syntactically constrained // ASTregexp node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // not destructive Fst resultFst = lib.OutputProjection(fst); stack.push(resultFst); return data; } public Object visit(ASTnet_close_sigma_func_call node, Object data) { // this function call is not destructive (i.e. returns a new net) // just $^__closeSigma($fst, $base) built-in, wrapped as // $^closeSigma($fst, $base="") // See also ASTnet_close_sigma_dest_func_call // two daughters: (constrained by the parser) // regexp // regexp node.childrenAccept(this, data); Fst base = (Fst) stack.pop(); Fst fst = (Fst) stack.pop(); // non-destructive if (fst.getFromSymtab()) { fst = lib.CopyFst(fst); } // first promotes the sigma of fst relative to base // then deletes the OTHER arcs in fst, in place lib.CloseSigmaInPlace(fst, base); stack.push(fst); return data; } public Object visit(ASTnet_close_sigma_dest_func_call node, Object data) { // this function call is destructive (i.e. always operated on the arg in place // just $^__closeSigma!($fst, $base) built-in, wrapped as // $^closeSigma!($fst, $base="") // See also ASTnet_close_sigma_func_call // two daughters: (constrained by the parser) // regexp // regexp node.childrenAccept(this, data); Fst base = (Fst) stack.pop(); Fst fst = (Fst) stack.pop(); // don't make a copy lib.CloseSigmaInPlace(fst, base); stack.push(fst); return data; } public Object visit(ASTnet_copy_func_call node, Object data) { // just $^__copy($arg) built-in // wrapped as $^copy($arg) in predefined.kl // should be just one daughter: arg_list node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); Fst resultFst = lib.CopyFst(fst); stack.push(resultFst); return data; } public Object visit(ASTnet_rm_weight_dest_func_call node, Object data) { // just $^__rmWeight!($arg) built-in // wrapped as $^rmWeight!($fst) in predefined.kl // one daughter, syntactically constrained // ASTregexp node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); lib.RmWeightDestFst(fst); // destructive stack.push(fst); return data; } public Object visit(ASTnet_rm_weight_func_call node, Object data) { // just $^__rmWeight($arg) built-in // wrapped as $^rmWeight($fst) in predefined.kl // one daughter, syntactically constrained // ASTregexp node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // RmWeightFst is a Java function, non-destructive // will copy fst only if necessary Fst resultFst = lib.RmWeightFst(fst); stack.push(resultFst); return data; } public Object visit(ASTnet_subst_symbol_dest_func_call node, Object data) { // just $^__substSymbol!($net, $old, $new) // wrapped as $^substSymbol!() in predefined.kl // exactly three daughters (syntactically constrained) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst resultFst = (Fst) stack.pop(); // do not copy--work on this Fst in place node.jjtGetChild(1).jjtAccept(this, data); Fst oldSymFst = (Fst) stack.pop(); // determinize, minimize and epsremove this net // even if user has set #KLEENEdeterminize, etc. to false lib.OptimizeInPlaceForce(oldSymFst); node.jjtGetChild(2).jjtAccept(this, data); Fst newSymFst = (Fst) stack.pop(); lib.OptimizeInPlaceForce(newSymFst); // oldSymFst should have a single arc and label if (!lib.IsString(oldSymFst) || (lib.NumArcs(oldSymFst) != 1)) { throw new KleeneArgException("Second arg to substSymbol!() must denote a one-arc acceptor."); } if (oldSymFst.getSigma().size() != 1) { throw new KleeneArgException("Second arg to substSymbol!() must be a normal symbol."); } // newSymFst should have a single arc and label, unless it // denotes the emptyStringLanguage, in which case the // cpv is 0 (wired in value of epsilon) int oldCpv; int newCpv; if (lib.IsEmptyStringLanguage(newSymFst)) { // epsilon special case // the Fst denotes the empty-string language // in OpenFst, 0 is wired in as the int value of epsilon newCpv = lib.Epsilon; } else { // the usual case, need a one-string, one-symbol fst if (!lib.IsString(newSymFst) || (lib.NumArcs(newSymFst) != 1)) { throw new KleeneArgException("Third arg to substSymbol!() must denote a one-arc acceptor."); } if (newSymFst.getSigma().size() != 1) { throw new KleeneArgException("Third arg to substSymbol!() must be a normal symbol."); } newCpv = ((Integer) newSymFst.getSigma().toArray()[0]).intValue(); } oldCpv = ((Integer) oldSymFst.getSigma().toArray()[0]).intValue(); lib.SubstLabelInPlace(resultFst, oldCpv, newCpv); stack.push(resultFst); return data; } public Object visit(ASTnet_subst_symbol_func_call node, Object data) { // just $^__substSymbol($net, $old, $new) // wrapped as $^substSymbol() in predefined.kl // exactly three daughters (syntactically constrained) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); Fst resultFst = fst; // non-destructive; copy if necessary if (fst.getFromSymtab()) { resultFst = lib.CopyFst(fst); } node.jjtGetChild(1).jjtAccept(this, data); Fst oldSymFst = (Fst) stack.pop(); // determinize, minimize and epsremove this net // even if user has set #KLEENEdeterminize, etc. to false lib.OptimizeInPlaceForce(oldSymFst); node.jjtGetChild(2).jjtAccept(this, data); Fst newSymFst = (Fst) stack.pop(); lib.OptimizeInPlaceForce(newSymFst); // oldSymFst should have a single arc and label if (!lib.IsString(oldSymFst) || (lib.NumArcs(oldSymFst) != 1)) { throw new KleeneArgException("Second arg to substSymbol() must denote a one-arc acceptor."); } if (oldSymFst.getSigma().size() != 1) { throw new KleeneArgException("Second arg to substSymbol() must be a normal symbol."); } // newSymFst should have a single arc and label, unless it // denotes the emptyStringLanguage, in which case the // cpv is 0 (wired in value of epsilon) int oldCpv; int newCpv; if (lib.IsEmptyStringLanguage(newSymFst)) { // epsilon special case // the Fst denotes the empty-string language newCpv = lib.Epsilon; } else { // the usual case, need a one-string, one-symbol fst if (!lib.IsString(newSymFst) || (lib.NumArcs(newSymFst) != 1)) { throw new KleeneArgException("Third arg to substSymbol() must denote a one-arc acceptor."); } if (newSymFst.getSigma().size() != 1) { throw new KleeneArgException("Third arg to substSymbol() must be a normal symbol."); } newCpv = ((Integer) newSymFst.getSigma().toArray()[0]).intValue(); } oldCpv = ((Integer) oldSymFst.getSigma().toArray()[0]).intValue(); lib.SubstLabelInPlace(resultFst, oldCpv, newCpv); stack.push(resultFst); return data; } public Object visit(ASTnet_eq_dest_func_call node, Object data) { // just $^__eq!($net, $old, $new) // wrapped as $^eq!() in predefined.kl // exactly three daughters (syntactically constrained) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst resultFst = (Fst) stack.pop(); // do not copy--work on this Fst in place node.jjtGetChild(1).jjtAccept(this, data); Fst leftDelimSymFst = (Fst) stack.pop(); // determinize, minimize and epsremove this net // even if user has set #KLEENEdeterminize, etc. to false lib.OptimizeInPlaceForce(leftDelimSymFst); node.jjtGetChild(2).jjtAccept(this, data); Fst rightDelimSymFst = (Fst) stack.pop(); lib.OptimizeInPlaceForce(rightDelimSymFst); // leftDelimSymFst should have a single arc and label if (!lib.IsString(leftDelimSymFst) || (lib.NumArcs(leftDelimSymFst) != 1)) { throw new KleeneArgException("Second arg to eq!() must denote a one-arc acceptor."); } if (leftDelimSymFst.getSigma().size() != 1) { throw new KleeneArgException("Second arg to eq!() must be a normal symbol."); } int leftDelimCpv = ((Integer) leftDelimSymFst.getSigma().toArray()[0]).intValue(); // rightDelimSymFst should have a single arc and label if (!lib.IsString(rightDelimSymFst) || (lib.NumArcs(rightDelimSymFst) != 1)) { throw new KleeneArgException("Third arg to eq!() must denote a one-arc acceptor."); } if (rightDelimSymFst.getSigma().size() != 1) { throw new KleeneArgException("Third arg to eq!() must be a normal symbol."); } int rightDelimCpv = ((Integer) rightDelimSymFst.getSigma().toArray()[0]).intValue(); lib.EqRedupInPlace(resultFst, leftDelimCpv, rightDelimCpv); stack.push(resultFst); return data; } public Object visit(ASTnet_eq_func_call node, Object data) { // just $^__eq($net, $old, $new) // wrapped as $^eq() in predefined.kl // exactly three daughters (syntactically constrained) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); Fst resultFst = fst; // non-destructive function; copy the first arg if necessary if (fst.getFromSymtab()) { resultFst = lib.CopyFst(fst); } node.jjtGetChild(1).jjtAccept(this, data); Fst leftDelimSymFst = (Fst) stack.pop(); // determinize, minimize and epsremove this net // even if user has set #KLEENEdeterminize, etc. to false lib.OptimizeInPlaceForce(leftDelimSymFst); node.jjtGetChild(2).jjtAccept(this, data); Fst rightDelimSymFst = (Fst) stack.pop(); lib.OptimizeInPlaceForce(rightDelimSymFst); // leftDelimSymFst should have a single arc and label if (!lib.IsString(leftDelimSymFst) || (lib.NumArcs(leftDelimSymFst) != 1)) { throw new KleeneArgException("Second arg to eq() must denote a one-arc acceptor."); } if (leftDelimSymFst.getSigma().size() != 1) { throw new KleeneArgException("Second arg to eq() must be a normal symbol."); } int leftDelimCpv = ((Integer) leftDelimSymFst.getSigma().toArray()[0]).intValue(); // rightDelimSymFst should have a single arc and label if (!lib.IsString(rightDelimSymFst) || (lib.NumArcs(rightDelimSymFst) != 1)) { throw new KleeneArgException("Third arg to eq() must denote a one-arc acceptor."); } if (rightDelimSymFst.getSigma().size() != 1) { throw new KleeneArgException("Third arg to eq() must be a normal symbol."); } int rightDelimCpv = ((Integer) rightDelimSymFst.getSigma().toArray()[0]).intValue(); lib.EqRedupInPlace(resultFst, leftDelimCpv, rightDelimCpv); stack.push(resultFst); return data; } public Object visit(ASTnet_read_xml_func_call node, Object data) { // just $^__readXml($filepath) built-in // wrapped with $^readXml($filepath) // just one daughter for $^__readXml() (syntactically // constrained by the parser) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst pathFst = (Fst) stack.pop(); String userTyped = lib.GetSingleString(pathFst, "First arg to readXml must denote a language of exactly one string, denoting a file path"); String fullpath = getFullpath(userTyped); Fst resultFst; try { resultFst = xml2fst(fullpath); // xml2fst, Java function, see above } catch (Exception e) { // catch the hard Exception and // throw a RuntimeException here, so that Kleene can recover throw new FuncCallException("Problem in $^readXml() reading indicated file."); } stack.push(resultFst); return data; } public Object visit(ASTnet_rand_gen_func_call node, Object data) { // $^randGen($fst, npath, max_length) node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) stack.pop(); long npath; long max_length; // second arg is the number of paths to leave in the resultFst node.jjtGetChild(1).jjtAccept(this, data); // leaves a Long or Double object on the stack Object obj = stack.pop(); // get long from either Long or Double if (obj instanceof Long) { npath = ((Long) obj).longValue(); } else { npath = ((Double) obj).longValue(); } // third arg is the maximum length of any single path node.jjtGetChild(2).jjtAccept(this, data); // leaves a Long or Double object on the stack obj = stack.pop(); // get long from either Long or Double if (obj instanceof Long) { max_length = ((Long) obj).longValue(); } else { max_length = ((Double) obj).longValue(); } // compute the random subset of fst Fst resultFst = lib.RandGen(fst, npath, max_length); stack.push(resultFst); return data; } public Object visit(ASTnet_start_func_call node, Object data) { // syntax: $^start($>foo) a built-in function-like regexp // Not really implemented as a function, cannot be aliased, i.e. // $^debut = $^start is not legal // The value of the statement, if successful, is an Fst. // just one daughter: ASTrrprod_id, which includes the name // of the start production // (This is constrained syntactically by the parser) // Don't evaluate daughter. Just retrieve the image. String rrprod_id = ((ASTrrprod_id) node.jjtGetChild(0)).getImage(); // The rrprod_id argument to $^start() will be the root of the // right-linear grammar, // but rrprod_id may refer to other productions, which may, // in turn, refer // to other productions, etc. (even circular references). // Need to check that all the RrDependencies ("right-recursive") // of $>foo are defined, // and that all the dependencies of the dependencies are defined, // etc. // Check and list dependencies (the productions) // for the whole implied grammar, keep them in an ArrayList // (I tried to use a HashSet, but this proved to be // impossible to iterate through AND increase in size) ArrayList<String> dependencies = new ArrayList<String>(); dependencies.add(rrprod_id); // Start with the current rrprod_id (the start); use ArrayList // so that dependencies of the overall grammar are added only // once (no duplicates). The order of objects in the ArrayList // is constant and starts at index 0 // Now loop through the ArrayList of dependencies, adding new ones // as they appear // (use a for-loop so that the size can grow during iteration-- // tried to use HashSet, but this proved impossible) for (int i = 0; i < dependencies.size(); i++) { String dep = dependencies.get(i); // Look up the dependency name (getting back an RrProdObject) // if successful (i.e. is in the symbol table). RrProdObject rrProdObject = (RrProdObject) env.get(dep); if (rrProdObject == null) { throw new UndefinedIdException("Undefined rrprod_id: " + dep); } // also check net_id and other _id references? or catch during // interpretation? // The dependencies of each defined production are stored // in the symbol table as part of the RrProdObject, // as a HashSet HashSet<String> hs = rrProdObject.getRrDependencies(); if (!hs.isEmpty()) { for (Iterator<String> iter = hs.iterator(); iter.hasNext();) { String s = iter.next(); // if the overall list of dependencies does not yet // contain s, then add it if (!dependencies.contains(s)) { dependencies.add(s); } } } } // Reaching here, the whole Rr grammar has been defined. // (All the required Rr productions are available in the // symbol table.) // Create an Fst result. // Need to copy all the states and arcs of networks // of the productions // into the result network, keeping track of the // new startStateNum of each network. // (This is a modification of the concatenation // algorithm of OpenFst, minus the code that creates an // epsilon arc from the final state(s) of the first // network to the start state of the second. // // Try to compile each network in the grammar. Initially // each right-recursive reference $>foo is treated much // like a multichar-symbol, but with // a negative code point value (a negative label value on an arc). // These negative code point values should not be added to // the sigma. Fst resultFst = null; // Instead of a HashMap, use two parallel ArrayLists, // later converted to int[], to pass easily to a C++ // native function that stitches the network together. // ArrayLists expand as necessary; avoid any preconceived // size limit. ArrayList<Integer> keys = new ArrayList<Integer>(); ArrayList<Integer> vals = new ArrayList<Integer>(); // will have neg ints (representing $>foo refs) mapped // to positive numbers corresponding to start states of the // various dependencies (productions) in the overall grammar. // Again loop through the list of dependencies (the names // of all the productions in the implied grammar); // they are "defined" (stored as ASTs) but not yet // "evaluated" into FSTs for (int i = 0; i < dependencies.size(); i++) { String rrprod = dependencies.get(i); RrProdObject rrProdObject = (RrProdObject) env.get(rrprod); // get the AST (.getRHS()), // and evaluate it to produce an Fst object ASTrrProdRHS astRrProdRHS = rrProdObject.getRHS(); astRrProdRHS.jjtAccept(this, data); // should leave an Fst object on the stack, for one production Fst fst = (Fst) stack.pop(); int startStateNum; if (i == 0) { // the zeroth is the root production resultFst = lib.CopyFst(fst); startStateNum = lib.StartState(resultFst); } else { // returns the _new_ start state number of fst startStateNum = lib.AddStatesAndArcsInPlace(resultFst, fst); } // A rrprod_id like $>foo is stored with a NEGATIVE int value // on an Fst arc. int negcpv = symmap.getint(rrprod); // effectively create a Map from neg. int keys // (right-linear labels)to non-neg. // integers that represent the new state number of the start // state of this particular Fst (for one of the productions // of the overall grammar); keys.add(negcpv); // will be a negative int value vals.add(startStateNum); } // Now need to "stitch" it all together lib.RrGrammarLinkInPlace(resultFst, keys, vals); stack.push(resultFst); return data; } public Object visit(ASTnet_parallel_func_call node, Object data) { // $^parallel(rule, rule ...) wired in "special form" // two or more daughters--syntactically limited to alternation rules int ruleCount = node.jjtGetNumChildren(); node.childrenAccept(this, data); // each rule should interpret as a List of RuleSemanticParts objects, // pushed on the stack; collect them ArrayList<RuleSemanticParts> collection = new ArrayList<RuleSemanticParts>(); for (int i = 0; i < ruleCount; i++) { collection.addAll((ArrayList<RuleSemanticParts>) stack.pop()); } // collection is a flat list of RuleSemanticParts from all the rules // under $^parallel(rule, rule, ...) // the result is one Fst representing all the parallel rules Fst parallelFst = compileRuleSemanticParts(collection); stack.push(parallelFst); return data; } public Object visit(ASTlng_pathcount_func_call node, Object data) { // just #^__pathCount($arg) built-in, wrapped as #^pathCount($arg) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); long count = lib.NumPaths(fst); // returns -1 for cyclic networks stack.push(new Long(count)); return data; } public Object visit(ASTlng_statecount_func_call node, Object data) { // just #^__stateCount($arg) built-in, wrapped as #^stateCount($arg) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); int count = lib.NumStates(fst); // in Kleene, all ints are stored internally as Long stack.push(new Long(count)); return data; } public Object visit(ASTlng_arccount_func_call node, Object data) { // just #^__arcCount($arg) built-in, wrapped as #^arcCount($arg) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); int count = lib.NumArcs(fst); // in Kleene, ints are always stored as Long stack.push(new Long(count)); return data; } public Object visit(ASTlng_get_int_cpv_func_call node, Object data) { // just #^__getIntCpv($arg) built-in, wrapped as #^getIntCpv($arg) // should be just one daughter: arg_list with one net, one non-epsilon // symbol node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); // determinize, minimize and epsremove this net // even if user has set #KLEENEdeterminize, etc. to false lib.OptimizeInPlaceForce(fst); // removes any epsilon // should be an acceptor with a single arc and label if (!lib.IsString(fst) || (lib.NumArcs(fst) != 1)) { throw new KleeneArgException("Argument to #^getIntCpv() must denote a one-arc acceptor."); } if (fst.getSigma().size() != 1) { throw new KleeneArgException("Argument to #^getIntCpv() must be a normal symbol."); } int cpv = ((Integer) fst.getSigma().toArray()[0]).intValue(); // in Kleene, ints are always stored as Long stack.push(new Long(cpv)); return data; } public Object visit(ASTlng_arity_func_call node, Object data) { // just #^__arity($arg) built-in, wrapped as #^arity($arg) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsAcceptor(fst)) stack.push(new Long(1)); else stack.push(new Long(2)); return data; } // boolean functions public Object visit(ASTlng_is_rtn_func_call node, Object data) { // just #^__isRtn($arg) built-in, wrapped as #^isRtn($arg) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getIsRtn()) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_cyclic_func_call node, Object data) { // just #^__isCyclic($net) built-in, wrapped as #^isCyclic($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsCyclic(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_ubounded_func_call node, Object data) { // just #^__isUBounded($net) built-in, wrapped as #^isUBounded($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsUBounded(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_lbounded_func_call node, Object data) { // just #^__isLBounded($net) built-in, wrapped as #^isLBounded($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsLBounded(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_acceptor_func_call node, Object data) { // just #^__isAcceptor($net) built-in, wrapped as #^isAcceptor($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsSemanticAcceptor(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_transducer_func_call node, Object data) { // just #^__isTransducer($net) built-in, wrapped as #^isTransducer($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (!(lib.IsSemanticAcceptor(fst))) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_output_labels_include_cpv_func_call node, Object data) { // just #^__outputLabelsIncludeCpv($fsm, #cpv) built-in, wrapped as // #^outputLabelsIncludeCpv($fsm, #cpv) // should be two daughters: regexp, numexp (int expected) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); node.jjtGetChild(1).jjtAccept(this, data); // leaves a Long or Double object on the stack (should be Long, // but can't restrain it syntactically) Object obj = stack.pop(); int cpv = 0; if (obj instanceof Long) { cpv = ((Long) obj).intValue(); } else { // KRB: throw an exception in this case? // unlikely case--get the intValue() cpv = ((Double) obj).intValue(); } if (lib.OutputLabelsIncludeCpv(fst, cpv)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_weighted_func_call node, Object data) { // just #^__isWeighted($net) built-in, wrapped as #^isWeighted($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsWeighted(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_ideterministic_func_call node, Object data) { // just #^__isIDeterministic($net) built-in, wrapped as #^isIDeterministic($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsIDeterministic(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_odeterministic_func_call node, Object data) { // just #^__isODeterministic($net) built-in, wrapped as #^isODeterministic($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsODeterministic(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_epsilonfree_func_call node, Object data) { // just #^__isEpsilonFree($net) built-in, wrapped as #^isEpsilonFree($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsEpsilonFree(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_empty_language_func_call node, Object data) { // just #^__isEmptyLanguage($net) built-in, wrapped as #^isEmptyLanguage($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsEmptyLanguage(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_empty_string_language_func_call node, Object data) { // just #^__isEmptyStringLanguage($net) built-in, wrapped as #^isEmptyStringLanguage($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsEmptyStringLanguage(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_contains_empty_string_func_call node, Object data) { // just #^__containsEmptyString($net) built-in, wrapped as #^containsEmptyString($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.ContainsEmptyString(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_is_string_func_call node, Object data) { // just #^__isString($net) built-in, wrapped as #^isString($net) and // #^isSingleStringLanguage($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (lib.IsString(fst)) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } public Object visit(ASTlng_contains_other_func_call node, Object data) { // just #^__containsOther($net) built-in, wrapped as #^containsOther($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data); // leaves an Fst object on the stack Fst fst = (Fst) stack.pop(); if (fst.getContainsOther()) stack.push(new Long(1)); else stack.push(new Long(0)); return data; } /* not needed? see definition in predefined.kl public Object visit(ASTlng_is_universal_language_func_call node, Object data) { // just #^__isUniversalLanguage($net) built-in, wrapped as #^isUniversalLanguage($net) // should be just one daughter: arg_list with one net node.jjtGetChild(0).jjtAccept(this, data) ; // leaves an Fst object on the stack Fst fst = (Fst) stack.pop() ; // IsUniversalLanguage doesn't exist yet if (lib.IsUniversalLanguage(fst)) stack.push(new Long(1)) ; else stack.push(new Long(0)) ; return data ; } */ // equivalent is used for acceptors, epsilon-free and deterministic public Object visit(ASTlng_equivalent_func_call node, Object data) { // just #^__equivalent($a, $b, #delta) built-in, wrapped as #^equivalent() // should be just three daughters, syntactically constrained node.childrenAccept(this, data); double delta; // OpenFst Equivalent calls for double delta // while the RandEquivalent calls for float delta Object delta_obj = stack.pop(); // third arg could be Long or Double if (delta_obj instanceof Long) { delta = ((Long) delta_obj).doubleValue(); } else { delta = ((Double) delta_obj).doubleValue(); } Fst b = (Fst) stack.pop(); Fst a = (Fst) stack.pop(); if (lib.Equivalent(a, b, delta)) stack.push(new Long(1)); // true else stack.push(new Long(0)); // false return data; } // rand_equivalent is used for transducers public Object visit(ASTlng_rand_equivalent_func_call node, Object data) { // just #^__randEquivalent($a, $b, #npath, #delta, #seed, #path_length) // built-in, wrapped as #^randEquivalent() // should be 6 daughters, syntactically constrained node.childrenAccept(this, data); int path_length; Object path_length_obj = stack.pop(); // 6th arg could be Long or Double if (path_length_obj instanceof Long) { path_length = ((Long) path_length_obj).intValue(); } else { path_length = ((Double) path_length_obj).intValue(); } int seed; Object seed_obj = stack.pop(); // 5th arg could be Long or Double if (seed_obj instanceof Long) { seed = ((Long) seed_obj).intValue(); } else { seed = ((Double) seed_obj).intValue(); } float delta; // the OpenFst RandEquivalent calls for float delta // while the OpenFst Equivalent calls for double delta // keep an eye on this Object delta_obj = stack.pop(); // 4th arg could be Long or Double if (delta_obj instanceof Long) { delta = ((Long) delta_obj).floatValue(); } else { delta = ((Double) delta_obj).floatValue(); } long npath; // the OpenFst RandEquivalent calls for float Object npath_obj = stack.pop(); // 3th arg could be Long or Double if (npath_obj instanceof Long) { npath = ((Long) npath_obj).longValue(); } else { npath = ((Double) npath_obj).longValue(); } Fst b = (Fst) stack.pop(); Fst a = (Fst) stack.pop(); if (lib.RandEquivalent(a, b, npath, delta, seed, path_length)) stack.push(new Long(1)); // true else stack.push(new Long(0)); // false return data; } public Object visit(ASTnum_abs_func_call node, Object data) { // just #^__abs(numexp) built-in // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object obj = stack.pop(); // produce either Long or Double, according to the input if (obj instanceof Long) { stack.push(new Long(Math.abs(((Long) obj).longValue()))); } else { stack.push(new Double(Math.abs(((Double) obj).doubleValue()))); } return data; } public Object visit(ASTnet_to_string_func_call node, Object data) { // convert a number (Long or Float) to a string // just $^__toString(numexp) built-in // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object obj = stack.pop(); String str; // produce either Long or Double, according to the input if (obj instanceof Long) { str = Long.toString(((Long) obj).longValue()); } else { str = Double.toString(((Double) obj).doubleValue()); } // Because all digits in a number string are in the BMP, // this str will be composed of BMP Unicode characters // convert to an array of int int len = str.length(); int[] cpvArray = new int[len]; for (int index = 0; index < len; index++) { int cpv = str.codePointAt(index); symmap.putsym(String.valueOf((char) cpv)); cpvArray[index] = cpv; } Fst resultFst = lib.FstFromCpvArray(cpvArray); stack.push(resultFst); return data; } public Object visit(ASTnet_char_for_cpv_func_call node, Object data) { // convert a number (Long or Float) to a character // just $^__charForCPV(numexp) built-in, wrapped as $^charForCPV(numexp) // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object obj = stack.pop(); int cpv; // produce a Long, representing a code point value if (obj instanceof Long) { cpv = ((Long) obj).intValue(); } else { cpv = ((Double) obj).intValue(); } symmap.putsym(String.valueOf((char) cpv)); stack.push(lib.OneArcFst(cpv)); return data; } public Object visit(ASTnet_implode_func_call node, Object data) { // The $^__implode(regexp) built-in, wrapped as $^implode(), // implodes a single string of characters into a single symbol, // typically a multichar symbol, // and return a network with one arc, labeled with that symbol // there should be just one daughter: arg_list with one regexp node.jjtGetChild(0).jjtAccept(this, data); Fst fstOrig = (Fst) stack.pop(); String str = lib.GetSingleString(fstOrig, "Arg to implode must denote a language of exactly one string."); if (str.length() == 0) { throw new KleeneArgException("Argument to implode must denote a language of one non-empty string."); } // get the code point value (cpv) int cpv = symmap.putsym(str); // might exist in symmap, or not // get back the int value // create a one-arc Fst with the arc labeled with the codepoint stack.push(lib.OneArcFst(cpv)); return data; } public Object visit(ASTnet_explode_func_call node, Object data) { // just #^__explode(regexp) built-in // take a two-state, one arc acceptor, get the label // on the one arc // (an int, typically mapping to a multichar name), // get the print name, // make a new one-path network for the exploded // string of characters in that name // there should be just one daughter, acceptor, // one string (non-empty), two states, one arc node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) stack.pop(); // get the single arc label int label = lib.GetSingleArcLabel(fst); // get the print name of the symbol // (typically a multichar symbol name) String str = symmap.getsym(label); // KRB // the str will be composed of BMP Unicode characters // (keep an eye on this--it // depends on what is allowed in a multichar symbol) int len = str.length(); // create an array of ints (code point values) int[] cpvArray = new int[len]; for (int index = 0; index < len; index++) { int cpv = str.codePointAt(index); symmap.putsym(String.valueOf((char) cpv)); cpvArray[index] = cpv; } Fst resultFst = lib.FstFromCpvArray(cpvArray); stack.push(resultFst); return data; } public Object visit(ASTnet_get_func_call node, Object data) { // $^__get($@arr, 0) wrapped as $^get($@arr, 0) // two daughters // net_list_exp // numexp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object obj = stack.pop(); int index; if (obj instanceof Long) { index = ((Long) obj).intValue(); } else { index = ((Double) obj).intValue(); } int size = netList.size(); if (index < 0 || index >= size) { throw new KleeneArgException("Illegal index to $^get(): " + index); } Fst element = netList.get(index); stack.push(element); return data; } public Object visit(ASTnet_getlast_func_call node, Object data) { // $^__getLast($@arr) wrapped as $^getLast($@arr) // two daughters // net_list_exp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); if (netList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } int size = netList.size(); Fst element = netList.get(size - 1); stack.push(element); return data; } public Object visit(ASTnum_get_func_call node, Object data) { // #^__get(#@arr, 0) wrapped as #^get(#@arr, 0) // two daughters // num_list_exp // numexp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object obj = stack.pop(); int index; if (obj instanceof Long) { index = ((Long) obj).intValue(); } else { index = ((Double) obj).intValue(); } int size = numList.size(); if (index < 0 || index >= size) { throw new KleeneArgException("Illegal index to #^get(): " + index); } Object element = numList.get(index); // Long or Double stack.push(element); return data; } public Object visit(ASTnum_getlast_func_call node, Object data) { // #^__getLast(#@arr) wrapped as #^getLast(#@arr) // one daughter // num_list_exp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); if (numList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } int size = numList.size(); Object element = numList.get(size - 1); // Long or Double stack.push(element); return data; } public Object visit(ASTnet_head_func_call node, Object data) { // $^__head($@arr) wrapped as $^head($@arr) // one daughter // net_list_exp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); if (netList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } int size = netList.size(); Fst element = netList.get(0); stack.push(element); return data; } public Object visit(ASTnum_head_func_call node, Object data) { // #^__head(#@arr) wrapped as #^head(#@arr) // one daughter // num_list_exp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); if (numList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } Object element = numList.get(0); stack.push(element); return data; } public Object visit(ASTnet_pop_dest_func_call node, Object data) { // $^__pop!($@arr) wrapped as $^pop!($@arr) // one daughter // net_list_exp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); if (netList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } Fst element = netList.pop(); stack.push(element); return data; } public Object visit(ASTnet_remove_dest_func_call node, Object data) { // $^__remove!($@arr, #index) // two daughters // net_list_exp // numexp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object obj = stack.pop(); int index = 0; if (obj instanceof Long) { index = ((Long) obj).intValue(); } else { index = ((Double) obj).intValue(); } NetList resultList = netList; int size = resultList.size(); if (index < 0 || index >= size) { throw new KleeneArgException("Illegal index to $^remove!(): " + index); } Fst removedElement = (Fst) (resultList.remove(index)); stack.push(removedElement); return data; } public Object visit(ASTnet_removelast_dest_func_call node, Object data) { // $^__removeLast!($@arr) // one daughter // net_list_exp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); NetList resultList = netList; if (netList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } int size = resultList.size(); Fst removedElement = (Fst) (resultList.remove(size - 1)); stack.push(removedElement); return data; } public Object visit(ASTnum_pop_dest_func_call node, Object data) { // #^__pop!(#@arr) wrapped as #^pop!(#@arr) // one daughter // num_list_exp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); if (numList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } Object removedElement = numList.pop(); stack.push(removedElement); return data; } public Object visit(ASTnum_remove_dest_func_call node, Object data) { // #^__remove!(#@arr, #index) // two daughters // num_list_exp // numexp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object obj = stack.pop(); int index = 0; if (obj instanceof Long) { index = ((Long) obj).intValue(); } else { index = ((Double) obj).intValue(); } NumList resultList = numList; int size = resultList.size(); if (index < 0 || index >= size) { throw new KleeneArgException("Illegal index to #^remove!(): " + index); } Object removedElement = resultList.remove(index); stack.push(removedElement); return data; } public Object visit(ASTnum_removelast_dest_func_call node, Object data) { // #^__removeLast!(#@arr) // one daughter // num_list_exp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); NumList resultList = numList; if (resultList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } int size = resultList.size(); Object removedElement = resultList.remove(size - 1); stack.push(removedElement); return data; } public Object visit(ASTslice_exp node, Object data) { // two numexp daughters node.childrenAccept(this, data); // should leave two objects on the stack (Long or Double) return data; } public Object visit(ASTnet_list_get_slice_func_call node, Object data) { // $^getSlice($@arr, 0, 3, 5:8), cannot be aliased // because it has a variable number of arguments // at least two daughters // net_list_exp // ( numexp || slice_exp )+ int childCount = node.jjtGetNumChildren(); node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); NetList resultList = new NetList(); int indexLow, indexHigh; Object obj, objLow, objHigh; int size; for (int i = 1; i < childCount; i++) { if (node.jjtGetChild(i) instanceof ASTnumexp) { node.jjtGetChild(i).jjtAccept(this, data); // should leave a Long or Double on the stack obj = stack.pop(); resultList.add(netList.get(getIntValue(obj))); } else if (node.jjtGetChild(i) instanceof ASTslice_exp) { node.jjtGetChild(i).jjtAccept(this, data); // should leave two objects on the stack, High on top of Low // could be Long or Double objHigh = stack.pop(); objLow = stack.pop(); indexLow = getIntValue(objLow); indexHigh = getIntValue(objHigh); // indexLow is inclusive; indexHigh is exclusive if (indexLow > indexHigh) { throw new KleeneArgException("Illegal relative values of range " + indexLow + ".." + indexHigh); } size = netList.size(); if (indexLow < 0 || indexLow >= size) { throw new KleeneArgException("Illegal low value of range " + indexLow + ".." + indexHigh); } if (indexHigh < 0 || indexHigh > size) { throw new KleeneArgException("Illegal high value of range " + indexLow + ".." + indexHigh); } for (int j = indexLow; j < indexHigh; j++) { resultList.add(netList.get(j)); } } } stack.push(resultList); return data; } public Object visit(ASTnum_list_get_slice_func_call node, Object data) { // #^getSlice(#@arr, 0, 3, 5:8), cannot be aliased // because it has a variable number of arguments // at least two daughters // num_list_exp // ( numexp || slice_exp )+ int childCount = node.jjtGetNumChildren(); node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); NumList resultList = new NumList(); int indexLow, indexHigh; Object obj, objLow, objHigh; int size; Object longOrDouble; for (int i = 1; i < childCount; i++) { if (node.jjtGetChild(i) instanceof ASTnumexp) { node.jjtGetChild(i).jjtAccept(this, data); // should leave a Long or Double on the stack obj = stack.pop(); longOrDouble = numList.get(getIntValue(obj)); if (longOrDouble instanceof Long) { resultList.add((Long) longOrDouble); } else { resultList.add((Double) longOrDouble); } } else if (node.jjtGetChild(i) instanceof ASTslice_exp) { node.jjtGetChild(i).jjtAccept(this, data); // should leave two objects on the stack, High on top of Low // could be Long or Double objHigh = stack.pop(); objLow = stack.pop(); indexLow = getIntValue(objLow); indexHigh = getIntValue(objHigh); // indexLow is inclusive; indexHigh is exclusive if (indexLow > indexHigh) { throw new KleeneArgException("Illegal relative values of range " + indexLow + ".." + indexHigh); } size = numList.size(); if (indexLow < 0 || indexLow >= size) { throw new KleeneArgException("Illegal low value of range " + indexLow + ".." + indexHigh); } if (indexHigh < 0 || indexHigh > size) { throw new KleeneArgException("Illegal high value of range " + indexLow + ".." + indexHigh); } for (int j = indexLow; j < indexHigh; j++) { longOrDouble = numList.get(j); if (longOrDouble instanceof Long) { resultList.add((Long) longOrDouble); } else { resultList.add((Double) longOrDouble); } } } } stack.push(resultList); return data; } public Object visit(ASTnet_list_push_dest_func_call node, Object data) { // $^__push!($fst, $@arr) // two daughters // regexp // net_list_exp node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); NetList resultList = netList; resultList.push(fst); stack.push(resultList); return data; } public Object visit(ASTnum_list_push_dest_func_call node, Object data) { // #^__push!(#num, #@arr) // two daughters // numexp // num_list_exp node.jjtGetChild(0).jjtAccept(this, data); Object longOrDouble = stack.pop(); node.jjtGetChild(1).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); NumList resultList = numList; if (longOrDouble instanceof Long) { resultList.push((Long) longOrDouble); } else { resultList.push((Double) longOrDouble); } stack.push(resultList); return data; } public Object visit(ASTnet_list_add_dest_func_call node, Object data) { // $^__add!($@arr, $fst) // two daughters // net_list_exp // regexp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); NetList resultList = netList; resultList.add(fst); stack.push(resultList); return data; } public Object visit(ASTnum_list_add_dest_func_call node, Object data) { // #^__add!(#@arr, #num) // two daughters // num_list_exp // numexp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object longOrDouble = stack.pop(); NumList resultList = numList; if (longOrDouble instanceof Long) { resultList.add((Long) longOrDouble); } else { resultList.add((Double) longOrDouble); } stack.push(resultList); return data; } public Object visit(ASTnet_list_addat_dest_func_call node, Object data) { // $^__add!($@arr, #index, $fst) // three daughters // net_list_exp // numexp // regexp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object obj = stack.pop(); int index = 0; if (obj instanceof Long) { index = ((Long) obj).intValue(); } else { index = ((Double) obj).intValue(); } node.jjtGetChild(2).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); NetList resultList = netList; int size = resultList.size(); if (index < 0 || index > size) { throw new KleeneArgException("Illegal index to $@^addAt!(): " + index); } resultList.addAt(index, fst); stack.push(resultList); return data; } public Object visit(ASTnum_list_addat_dest_func_call node, Object data) { // #^__add!(#@arr, #index, #num) // three daughters // num_list_exp // numexp // numexp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object obj = stack.pop(); int index = 0; if (obj instanceof Long) { index = ((Long) obj).intValue(); } else { index = ((Double) obj).intValue(); } node.jjtGetChild(2).jjtAccept(this, data); Object longOrDouble = stack.pop(); NumList resultList = numList; int size = resultList.size(); if (index < 0 || index > size) { throw new KleeneArgException("Illegal index to #@^addAt!(): " + index); } if (longOrDouble instanceof Long) { resultList.addAt(index, (Long) longOrDouble); } else { resultList.addAt(index, (Double) longOrDouble); } stack.push(resultList); return data; } public Object visit(ASTnet_list_set_dest_func_call node, Object data) { // $^__set!($@arr, #index, $fst) // three daughters // net_list_exp // numexp // regexp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object obj = stack.pop(); int index = 0; if (obj instanceof Long) { index = ((Long) obj).intValue(); } else { index = ((Double) obj).intValue(); } node.jjtGetChild(2).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); NetList resultList = netList; int size = resultList.size(); if (index < 0 || index >= size) { throw new KleeneArgException("Illegal index to $@^set!(): " + index); } resultList.set(index, fst); stack.push(resultList); return data; } public Object visit(ASTnum_list_set_dest_func_call node, Object data) { // #^__set!(#@arr, #index, #num) // three daughters // num_list_exp // numexp // numexp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); node.jjtGetChild(1).jjtAccept(this, data); Object obj = stack.pop(); int index = 0; if (obj instanceof Long) { index = ((Long) obj).intValue(); } else { index = ((Double) obj).intValue(); } node.jjtGetChild(2).jjtAccept(this, data); Object longOrDouble = stack.pop(); NumList resultList = numList; int size = resultList.size(); if (index < 0 || index >= size) { throw new KleeneArgException("Illegal index to #@^set!(): " + index); } if (longOrDouble instanceof Long) { resultList.set(index, (Long) longOrDouble); } else { resultList.set(index, (Double) longOrDouble); } stack.push(resultList); return data; } public Object visit(ASTnet_list_tail_func_call node, Object data) { // $^__tail($@arr) // one daughter // net_list_exp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); // as in Scheme, Haskell and Scala, fail if the argument // list is empty if (netList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } NetList resultList = new NetList(); int size = netList.size(); // new object has all but the zeroth element for (int i = 1; i < size; i++) { resultList.add(netList.get(i)); } stack.push(resultList); return data; } public Object visit(ASTnet_list_copy_func_call node, Object data) { // $^__copy($@arr) // one daughter // net_list_exp node.jjtGetChild(0).jjtAccept(this, data); NetList netList = (NetList) (stack.pop()); NetList resultList = new NetList(); int size = netList.size(); // shallow copy for (int i = 0; i < size; i++) { resultList.add(netList.get(i)); } stack.push(resultList); return data; } public Object visit(ASTnum_list_tail_func_call node, Object data) { // #^__tail(#@arr) // one daughter // num_list_exp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); // as in Scheme, Haskell and Scala, fail if the argument // list is empty if (numList.isEmpty()) { throw new KleeneArgException("The argument list is empty."); } NumList resultList = new NumList(); int size = numList.size(); Object obj = null; // new object has all but the zeroth element for (int i = 1; i < size; i++) { obj = numList.get(i); if (obj instanceof Long) { resultList.add((Long) obj); } else { resultList.add((Double) obj); } } stack.push(resultList); return data; } public Object visit(ASTnum_list_copy_func_call node, Object data) { // #^__copy(#@arr) // one daughter // num_list_exp node.jjtGetChild(0).jjtAccept(this, data); NumList numList = (NumList) (stack.pop()); NumList resultList = new NumList(); int size = numList.size(); Object obj = null; // shallow copy for (int i = 0; i < size; i++) { obj = numList.get(i); if (obj instanceof Long) { resultList.add((Long) obj); } else { resultList.add((Double) obj); } } stack.push(resultList); return data; } public Object visit(ASTnet_list_get_sigma_func_call node, Object data) { // $@^__getSigma($fst) // one daughter // regexp // return the sigma as an array of nets, one for each symbol in // the sigma (minus special chars starting "__" that should not be // considered when promoting OTHER) node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); NetList resultList = new NetList(); String specialSymbolPrefix = "__"; HashSet<Integer> sigma = fst.getSigma(); String symbolName = ""; if (!sigma.isEmpty()) { for (Iterator<Integer> iter = sigma.iterator(); iter.hasNext();) { int cpv = iter.next().intValue(); symbolName = symmap.getsym(cpv); if (!(symbolName.startsWith(specialSymbolPrefix))) { resultList.add(lib.OneArcFst(cpv)); } } } stack.push(resultList); return data; } public Object visit(ASTnum_list_get_sigma_func_call node, Object data) { // #@^__getSigma($fst) // one daughter // regexp // return the sigma as an array of Integer, one for each symbol in // the sigma (minus special chars starting "__" that should not be // considered when promoting OTHER) node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); NumList resultList = new NumList(); String specialSymbolPrefix = "__"; HashSet<Integer> sigma = fst.getSigma(); String symbolName = ""; if (!sigma.isEmpty()) { for (Iterator<Integer> iter = sigma.iterator(); iter.hasNext();) { int cpv = iter.next().intValue(); symbolName = symmap.getsym(cpv); if (!(symbolName.startsWith(specialSymbolPrefix))) { resultList.add(new Long(cpv)); } } } stack.push(resultList); return data; } public Object visit(ASTnet_get_net_func_call node, Object data) { // one regexp daughter, syntactically constrained // should represent a language of one string // return a handle to a network, given its name node.jjtGetChild(0).jjtAccept(this, data); Fst netNameFst = (Fst) (stack.pop()); lib.OptimizeInPlaceForce(netNameFst); //lib.FstDump(netNameFst) ; String net_id = lib.GetSingleString(netNameFst, "Argument to $^getNet() must denote a language of exactly one string"); if (net_id.length() == 0) { throw new KleeneArgException("Second arg to info (filepath) must denote a non-empty string"); } // with the String name of the fst, retrieve the Fst from the environment Fst fst = (Fst) env.get(net_id); if (fst != null) { // The net_id was found in a symbol table. // // Fst is a Java wrapper object around a long int that stores a // (basically C++) pointer to a network. Need to mark this // Fst as being "fromSymtab" if, as here, the value came from a // symbol table (and therefore cannot be changed, i.e. must be // persistent in case the net_id is referred to again). fst.setFromSymtab(true); // leave the Fst on the stack stack.push(fst); return data; } else { // attempt to refer to (use) an undefined variable throw new UndefinedIdException("Undefined net_id: " + net_id); } } public Object visit(ASTnet_sub_func_call node, Object data) { // one ASTnet_id daughter, constrained by the parser, carries the image // return a one-arc network that represents a reference to // a subnetwork (to appear inside an RTN) boolean openFstRtnConv = isOpenFstRtnConventions(); // currently either OpenFstRtnConventions, or // SapRtnConventions // In OpenFstRtnConventions, a reference to a subnet $foo is // an arc labeled $eps:'__$foo' // In SapRtnConventions, a reference to a subnet $foo is // an arc labeled '$foo':'$foo' (the user can also manually // encode $^sub($foo):$eps, which indicates, to the runtime // code, a mapping to epsilon) String specialCharPrefix = ""; if (openFstRtnConv) { specialCharPrefix = "__"; } else { specialCharPrefix = ""; } // get the image from the daughter String name = specialCharPrefix + ((ASTnet_id) node.jjtGetChild(0)).getImage(); if (openFstRtnConv) { stack.push(lib.OneArcFst(0, name)); // OpenFstRtnConventions } else { stack.push(lib.OneArcFst(name, name)); // SapRtnConventions } return data; } // currently works from the sigma, finding all __$ references whether they // are on the input or output side private void findDependencies(Fst fst, ArrayList<String> dependencies) { String subnetReferencePrefix = ""; if (isOpenFstRtnConventions()) { subnetReferencePrefix = "__$"; } else { subnetReferencePrefix = "$"; } HashSet<Integer> sigma = fst.getSigma(); String symbolName = ""; String netIdName = ""; if (!sigma.isEmpty()) { for (Iterator<Integer> iter = sigma.iterator(); iter.hasNext();) { // the name should look like "a", "b", and perhaps // "__$sub" (for OpenFstRtnConventions) or "$sub" (for SapRtnConventions) symbolName = symmap.getsym(iter.next().intValue()); if (symbolName.startsWith(subnetReferencePrefix)) { // save the subnet name, minus any prefix netIdName = symbolName.substring(symbolName.indexOf("$")); // avoid duplicates if (!dependencies.contains(netIdName)) { dependencies.add(netIdName); } } } } } public Object visit(ASTnet_embed_rtn_subnets_func_call node, Object data) { // evaluate the argument, a regexp, leaving an Fst object on the stack node.jjtGetChild(0).jjtAccept(this, data); Fst baseFst = (Fst) (stack.pop()); // Check and list dependencies (the subnetworks) // for the whole implied RTN, keep them in an ArrayList // (HashSet would be convenient, but you can't iterate through it // and add objects to it during the iteration). HashSet would keep // out duplicates automatically--it's a bit harder with ArrayList. ArrayList<String> dependencies = new ArrayList<String>(); // find all subnets referred to by the baseFst findDependencies(baseFst, dependencies); // The order of objects in the ArrayList is constant and starts at index 0 // Now loop through the ArrayList of dependencies, which is a set (no // duplicates) adding new dependencies as they appear // (use a for-loop so that the size can grow during iteration-- // I tried to use HashSet, but this proved impossible) String dep; // keep a set of dependencies that are not defined (any // undefined dependency is an error) HashSet<String> not_defined = new HashSet<String>(); for (int i = 0; i < dependencies.size(); i++) { dep = dependencies.get(i); // Look up the dependency name in the symbol table. Fst fst = (Fst) env.get(dep); if (fst == null) { // add it to the set of undefined dependencies not_defined.add(dep); continue; } // find any additional dependencies of this dependency findDependencies(fst, dependencies); } // if any dependencies are not defined, throw an exception if (!not_defined.isEmpty()) { throw new UndefinedIdException("Undefined networks: " + not_defined.toString()); } // Reaching here, the whole RTN grammar has been defined. // (All the required networks are available in the // symbol table.) // Create an Fst result that incorporates the subnetworks, // unioning them in (with special prefixes) with the base // network. // EmbeddedRtn is not destructive; copies baseFst // if it comes from a symbol table Fst resultFst = lib.EmbeddedRtn(baseFst, dependencies, "__SUBNETWORKS"); stack.push(resultFst); return data; } // current used only by net_expand_rtn_func, which works only // with OpenFstRtnConventions private void findSubnetReferences(Fst fst, ArrayList<String> subnetReferences) { String subnetReferencePrefix = "__$"; // look for output-side symbols that refer to subnetworks // first need to get the output-side sigma int[] outputSigmaArray = lib.GetOutputLabels(fst); String symbolName; String netIdName; for (int j = 0; j < outputSigmaArray.length; j++) { symbolName = symmap.getsym(outputSigmaArray[j]); if (symbolName.startsWith(subnetReferencePrefix)) { // strip the prefix netIdName = symbolName.substring(symbolName.indexOf("$")); // avoid duplicates if (!subnetReferences.contains(netIdName)) { System.out.println("New reference: " + netIdName); subnetReferences.add(netIdName); } } } } public Object visit(ASTnet_expand_rtn_func_call node, Object data) { // syntax $^expandRtn(regexp) if (lib.isSapRtnConventions()) { throw new KleeneInterpreterException("$^expandRtn() is not yet implemented under SapRtnConventions"); } // evaluate the one argument, a regexp, // leaving an Fst object on the stack node.jjtGetChild(0).jjtAccept(this, data); Fst baseFst = (Fst) (stack.pop()); int baseFstInt; if (baseFst.getFromSymtab()) { // then reach down into the AST to get the image String net_id = ((ASTnet_id) (node.jjtGetChild(0).jjtGetChild(0))).getImage(); baseFstInt = symmap.putsym("__" + net_id); } else { baseFstInt = -1000000; // KRB: magic number } // a HashSet would be more convenient, but you can't iterate through // it and add to it ArrayList<String> subnetReferences = new ArrayList<String>(); // find subnet references in the baseFst findSubnetReferences(baseFst, subnetReferences); // The order of objects in the ArrayList is constant and starts at index 0 // Now loop through the ArrayList of subnetReferences, adding new ones // as they appear // (use a for-loop so that the size can grow during iteration-- // I tried to use HashSet and an Iterator, but this proved impossible) // Collect a set of undefined networks (if any) HashSet<String> not_defined = new HashSet<String>(); String subnetName; Fst subFst; for (int i = 0; i < subnetReferences.size(); i++) { subnetName = subnetReferences.get(i); // Look up the subnet name in the symbol table. subFst = (Fst) env.get(subnetName); if (subFst == null) { // add it to the list not_defined.add(subnetName); continue; } // also look for subnet references in this subnet findSubnetReferences(subFst, subnetReferences); } // bail out here, with a useful Exception message, if any // of the required subnets are not defined. if (!not_defined.isEmpty()) { throw new UndefinedIdException("Failed RTN expansion, undefined networks: " + not_defined.toString()); } // Reaching here, the whole RTN grammar has been defined. // (All the required networks are available in the // symbol table.) Fst resultFst = lib.ExpandRtn(baseFst, baseFstInt, subnetReferences); stack.push(resultFst); return data; } public Object visit(ASTdbl_ceil_func_call node, Object data) { // just #^__ceil(numexp) built-in // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object o = stack.pop(); // Math.ceil() always produces Double if (o instanceof Long) { stack.push(new Double(Math.ceil(((Long) o).doubleValue()))); } else { stack.push(new Double(Math.ceil(((Double) o).doubleValue()))); } return data; } public Object visit(ASTdbl_floor_func_call node, Object data) { // just #^__floor(numexp) built-in // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object o = stack.pop(); // Math.floor() always produces Double if (o instanceof Long) { stack.push(new Double(Math.floor(((Long) o).doubleValue()))); } else { stack.push(new Double(Math.floor(((Double) o).doubleValue()))); } return data; } public Object visit(ASTlng_round_func_call node, Object data) { // just #^__round(numexp) built-in // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object o = stack.pop(); // Math.round() always produces Long from a Double arg if (o instanceof Long) { stack.push(new Long(Math.round(((Long) o).doubleValue()))); } else { stack.push(new Long(Math.round(((Double) o).doubleValue()))); } return data; } public Object visit(ASTlng_long_func_call node, Object data) { // basically a cast to Long // just #^__long(numexp) built-in, wrapped as #^long() and $^int() // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object o = stack.pop(); // always produce Long if (o instanceof Long) { stack.push(new Long(((Long) o).longValue())); } else { stack.push(new Long(((Double) o).longValue())); } return data; } public Object visit(ASTlng_size_func_call node, Object data) { // just #^size(exp) wired-in // one daughter // NetList or NumList node.jjtGetChild(0).jjtAccept(this, data); Object list = stack.pop(); Long size; if (list instanceof NetList) { NetList netList = (NetList) list; size = new Long((long) (netList.size())); } else if (list instanceof NumList) { NumList numList = (NumList) list; size = new Long((long) (numList.size())); } else { throw new KleeneArgException("Type problem with argument to $^size"); } stack.push(size); return data; } public Object visit(ASTlng_is_empty_func_call node, Object data) { // one daughter // NetList or NumList node.jjtGetChild(0).jjtAccept(this, data); Object list = stack.pop(); long size; if (list instanceof NetList) { size = (long) (((NetList) list).size()); } else if (list instanceof NumList) { size = (long) (((NumList) list).size()); } else { throw new KleeneArgException("Type problem with argument to $^isEmpty"); } if (size == 0L) { // isEmpty is true stack.push(new Long(1L)); } else { // isEmpty is false stack.push(new Long(0L)); } return data; } public Object visit(ASTdbl_double_func_call node, Object data) { // basically a cast to Double // just #^__double(numexp) built-in, // wrapped as #^double() and #^float() // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object o = stack.pop(); // always produce Long if (o instanceof Long) { stack.push(new Double(((Long) o).doubleValue())); } else { stack.push(new Double(((Double) o).doubleValue())); } return data; } public Object visit(ASTdbl_rint_func_call node, Object data) { // just #^__rint(numexp) built-in // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object o = stack.pop(); // Math.rint(double) always produce Double if (o instanceof Long) { stack.push(new Double(Math.rint(((Long) o).doubleValue()))); } else { stack.push(new Double(Math.rint(((Double) o).doubleValue()))); } return data; } public Object visit(ASTdbl_log_func_call node, Object data) { // natural log (Java Math.log(double d) // just #^__log(numexp) built-in, // wrapped as #^log(#num) // should be just one daughter: arg_list with one numexp node.jjtGetChild(0).jjtAccept(this, data); // leaves a Long or Double object on the stack Object o = stack.pop(); // always produce Long if (o instanceof Long) { stack.push(Math.log(((Long) o).doubleValue())); } else { stack.push(Math.log(((Double) o).doubleValue())); } return data; } public Object visit(ASTnet_func_exp node, Object data) { // daughter should be net_func_id or net_func_anon_exp or // net_func_func_call node.jjtGetChild(0).jjtAccept(this, data); // should leave a FuncValue object on the stack return data; } public Object visit(ASTvoid_func_exp node, Object data) { // daughter should be void_func_id or void_func_anon_exp or // void_func_func_call node.jjtGetChild(0).jjtAccept(this, data); // should leave a FuncValue object on the stack return data; } public Object visit(ASTnet_func_id node, Object data) { // this is called for net_func_id on the right-hand-side, // so it needs to successfully look up the FuncValue value // (or throw // an exception); for an assignment, e.g. $^func = // this visit method is not called String net_func_id = node.getImage(); // value from environment should be a FuncValue object Object obj = env.get(net_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined net_func_id: " + net_func_id); } return data; } public Object visit(ASTvoid_func_id node, Object data) { // this is called for void_func_id on the right-hand-side, // so it needs to successfully look up the value (or throw // an exception); for an assignment, e.g. ^func() = // this visit method is not called String void_func_id = node.getImage(); // value from environment should be a FuncValue object Object obj = env.get(void_func_id); if (obj != null) { stack.push(obj); return data; } else { throw new UndefinedIdException("Undefined void_func_id: " + void_func_id); } } public Object visit(ASTnet_func_anon_exp node, Object data) { // two daughters: param_list, func_block // collect them into a new FuncValue object // and push it on the stack // Don't evaluate the func_block, just get the AST itself node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // a FuncValue also needs to store a handle to the frame // in which it was defined (to get lexical scoping) stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } public Object visit(ASTvoid_func_anon_exp node, Object data) { // two daughters: param_list func_block // collect them into a FuncValue object and push it on the stack node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } public Object visit(ASTnet_func_func_call node, Object data) { // e.g. $^^add_suff(ing) or // $^^(....){...}(ing) // two daughters: net_func_func_exp arg_list // N.B. net_func_func_exp could be net_func_func_id or a // an anonymous function, // so it needs to be evaluated node.jjtGetChild(0).jjtAccept(this, data); // should leave a FuncValue object on the stack FuncValue funcValue = (FuncValue) stack.pop(); // now eval the argument list node.jjtGetChild(1).jjtAccept(this, data); // the object on top of the Stack is a count of the // function arguments ArgCounts ac = (ArgCounts) stack.pop(); // allocate a new Frame for the execution of the function call env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the arg values, in the new Frame try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all Exceptions and release the Frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block) try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the new Frame created for the // execution of this func call env.releaseFrame(); // Check return value; there should be an FuncValue // object left on the stack // by a return stmt in the funcBlock. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("NetFunc valued function call fails to return a net function."); } else if (!(obj instanceof FuncValue)) { throw new FuncCallException("NetFunc valued function call returns incorrect type."); } return data; } public Object visit(ASTvoid_func_func_call node, Object data) { // e.g. ^^add_suff(ing) or // ^^(....){...}(ing) // two daughters: void_func_func_exp arg_list // N.B. void_func_func_exp could be void_func_func_id or // an anonymous function, // so it needs to be evaluated node.jjtGetChild(0).jjtAccept(this, data); // should leave a FuncValue object on the stack FuncValue funcValue = (FuncValue) stack.pop(); // now eval the argument list node.jjtGetChild(1).jjtAccept(this, data); // the object on top of the Stack is a count of the // function arguments ArgCounts ac = (ArgCounts) stack.pop(); // allocate a new Frame for the execution of the function call env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the arg values, in the new Frame try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all Exceptions and release the Frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block) try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the new Frame created for the // execution of this func call env.releaseFrame(); // Check return value; there should be an FuncValue object left on the stack // by a return stmt in the funcBlock. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("VoidFunc valued function call fails to return a void function."); } else if (!(obj instanceof FuncValue)) { throw new FuncCallException("VoidFunc valued function call returns incorrect type."); } return data; } public Object visit(ASTnet_func_func_exp node, Object data) { // daughter should be net_func_func_id or net_func_func_anon_exp node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTvoid_func_func_exp node, Object data) { // daughter should be void_func_func_id // or void_func_func_anon_exp node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTnet_func_func_anon_exp node, Object data) { // two daughters: param_list func_block node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } public Object visit(ASTvoid_func_func_anon_exp node, Object data) { // two daughters: param_list func_block node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } // public Object visit(ASTarr_index_exp node, Object data) { // System.out.println("Interp: ASTarr_index_exp node not implemented.") ; // // e.g. (1) // return data ; // } // public Object visit(ASTnet_list_ref node, Object data) { // System.out.println("Interp: ASTnet_list_ref node not implemented.") ; // return data ; // } public Object visit(ASTnet_list_exp node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); // leave the result (a handle to a NetList) on the stack return data; } public Object visit(ASTnet_list_id node, Object data) { // Called only when the net_list_id is on the RHS (_not_ the LHS) // of an assignment statement, e.g. $@foo String net_list_id = node.getImage(); // with the String name, retrieve the NetList from the environment NetList netList = (NetList) env.get(net_list_id); if (netList != null) { // The net_list_id was found in a symbol table. // // NetList is a Java wrapper object around LinkedList<Fst> // that stores handles to a set of Fst objects. // Need to mark this // Array as being "fromSymtab" if, as here, the value came from a // symbol table (and therefore cannot be changed, i.e. must be // persistent in case the net_id is referred to again). netList.setFromSymtab(true); // leave the NetList on the stack stack.push(netList); } else { // attempt to refer to (use) an undefined variable throw new UndefinedIdException("Undefined net_list_id: " + net_list_id); } return data; } public Object visit(ASTnet_list_lit node, Object data) { // zero or more regexp daughters // $@(a, b, $bar) NetList netList = new NetList(); int childcount = node.jjtGetNumChildren(); Fst fst = null; for (int i = 0; i < childcount; i++) { node.jjtGetChild(i).jjtAccept(this, data); fst = (Fst) (stack.pop()); netList.add(fst); } // leave the result on the stack stack.push(netList); return data; } public Object visit(ASTnet_list_func_call node, Object data) { // Usual Syntax: $@^myunion(a, b) has a NetList value // // net_list_func_call always has two daughters: // net_list_func_exp // arg_list // // N.B. net_list_func_exp could be // net_list_func_id e.g. $@^myfunc OR // net_list_func_anon_exp e.g. $^(...){...} // so net_list_func_exp needs to be evaluated // Evaluate daughter 0, the net_list_func_exp node.jjtGetChild(0).jjtAccept(this, data); // Should leave a FuncValue on the stack FuncValue funcValue = (FuncValue) stack.pop(); // now evaluate the arg_list, which can have 0 to 2 daughters // arg_list // positional_args // named_args // If either daughter is present, it is non-empty. // If both daughters are present, // positional_args is always before named_args // N.B. the arguments have to be evaluated in the current Frame, // before allocating a new daughter frame for the execution of the // function body. // Evaluate daughter 1, the arg_list node.jjtGetChild(1).jjtAccept(this, data); // should leave on the stack, from top down // 1. an ArgCounts object (containing positional_args_count // and named_args_count) // 2. the positional arguments (in syntactic order), // the number being positional_args_count // 3. the named arguments (each represented by a NamedArg object), // the number being named_args_count // // first pop off the ArgCounts object, // leaving the evaluated args (if any) on the stack ArgCounts ac = (ArgCounts) stack.pop(); // Now allocate a new Frame for execution of this function call // (N.B. released below when the function returns) env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the arg values, in the new Frame try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all Exceptions and release the frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block); more precisely, // send a message to the function block telling it to accept this // IntepreterVisitor try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the Frame created for the execution of this func call env.releaseFrame(); // Check return value; // there should be a NetList object left on the stack // (this is ASTnet_list_func_call) by a return stmt in the funcBlock. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("Net list valued function call failed to return a net list."); } else if (!(obj instanceof NetList)) { throw new FuncCallException("Net list valued function call returns incorrect type."); } return data; } public Object visit(ASTnet_list_func_exp node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTnet_list_func_id node, Object data) { // this is called for net_list_func_id on the right-hand-side, // so it needs to successfully look up the FuncValue value // (or throw // an exception); for an assignment, e.g. $@^func = // this visit method is not called on the LHS String net_list_func_id = node.getImage(); // value from environment should be a FuncValue object Object obj = env.get(net_list_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined net_list_func_id: " + net_list_func_id); } return data; } public Object visit(ASTnet_list_func_anon_exp node, Object data) { // two daughters: param_list, func_block // collect them into a new FuncValue object // and push it on the stack // Don't evaluate the func_block, just get the AST itself node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // a FuncValue also needs to store a handle to the frame // in which it was defined (to get lexical scoping) stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } public Object visit(ASTnet_list_func_func_anon_exp node, Object data) { // two daughters: param_list, func_block // collect them into a new FuncValue object // and push it on the stack // Don't evaluate the func_block, just get the AST itself node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // a FuncValue also needs to store a handle to the frame // in which it was defined (to get lexical scoping) stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } public Object visit(ASTnum_list_func_func_anon_exp node, Object data) { // two daughters: param_list, func_block // collect them into a new FuncValue object // and push it on the stack // Don't evaluate the func_block, just get the AST itself node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // a FuncValue also needs to store a handle to the frame // in which it was defined (to get lexical scoping) stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } // public Object visit(ASTnum_list_ref node, Object data) { // System.out.println("Interp: ASTnum_func_ref node not implemented.") ; // // e.g. #@mylist(3) // return data ; // } public Object visit(ASTnum_list_exp node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); // leave the result (a handle to a NumList) on the stack return data; } public Object visit(ASTnum_list_id node, Object data) { // Called only when the num_list_id is on the RHS (_not_ the LHS) // of an assignment statement, e.g. #@foo String num_list_id = node.getImage(); // with the String name, retrieve the NumList from the environment NumList numList = (NumList) env.get(num_list_id); if (numList != null) { // The num_list_id was found in a symbol table. // // NumList is a Java wrapper object around LinkedList<Object> // that stores handles to a set of Long or Double objects. // Need to mark this // Array as being "fromSymtab" if, as here, the value came from a // symbol table (and therefore cannot be changed, i.e. must be // persistent in case the net_id is referred to again). numList.setFromSymtab(true); // leave the NumList on the stack stack.push(numList); } else { // attempt to refer to (use) an undefined variable throw new UndefinedIdException("Undefined num_list_id: " + num_list_id); } return data; } public Object visit(ASTnum_list_lit node, Object data) { // zero or more numexp daughters // #@(1, 2, #num) NumList numList = new NumList(); int childcount = node.jjtGetNumChildren(); Object obj = null; // will be Long or Double for (int i = 0; i < childcount; i++) { node.jjtGetChild(i).jjtAccept(this, data); obj = stack.pop(); // NumList has .add() methods for Long and Double if (obj instanceof Long) { numList.add((Long) obj); } else if (obj instanceof Double) { numList.add((Double) obj); } else { throw new KleeneArgException("Illegal value in number list literal."); } } // leave the result on the stack stack.push(numList); return data; } void rand_common(SimpleNode node, int projection, Object data) { // node: // required first arg is a regexp() // optional second arg is numexp() (npathval, the number of desired paths) // optional third arg is numexp() (max_lengthval) // projection // 0 for input projection // 1 for output projection // 2 for leave both projections (not implemented yet) // evaluate the first argument (a regexp()) node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); long npathval = 15L; // arbitrary magic number, default long max_lengthval = 50L; // arbitrary magic number, default int displayLimit = 500; // arbitrary magic number, default int childCount = node.jjtGetNumChildren(); if (childCount > 1) { // then npath is explicitly specified, e.g. 20 // (the user wants 20 words printed out) node.jjtGetChild(1).jjtAccept(this, data); // will leave a Long or Double on the stack Object obj = stack.pop(); if (obj instanceof Long) { npathval = ((Long) obj).longValue(); } else { npathval = ((Double) obj).longValue(); } } if (childCount > 2) { // then max_length is also explicitly specified node.jjtGetChild(2).jjtAccept(this, data); // will leave a Long or Double on the stack Object obj = stack.pop(); if (obj instanceof Long) { max_lengthval = ((Long) obj).longValue(); } else { max_lengthval = ((Double) obj).longValue(); } } // compute the random subset of fst Fst resultFst = lib.RandGen(fst, npathval, max_lengthval); String sepString = "\n"; long stringCount = lib.NumPaths(resultFst); PseudoTerminalInternalFrame terminal = null; if (((InterpData) data).getInGUI()) { terminal = ((InterpData) data).getGUI().getTerminal(); } if (stringCount == 0) { String msg = "(language is empty)"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } else if (stringCount == -1) { // then has loops, infinite language // SHOULD NOT BE POSSIBLE HERE String msg = "(language is infinite)"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } else if (stringCount <= displayLimit) { // then just list them (parameterize this figure later) if (terminal != null) { FstStringLister lister = new FstStringLister(terminal, symmap); // native function; second arg 0 is for input side, 1 for output side lib.ListAllStrings(resultFst, projection, lister); } else { FstSystemStringLister sysLister = new FstSystemStringLister(symmap, sepString); lib.ListAllStrings(resultFst, projection, sysLister); } } else { String msg = "(language exceeds displayLimit: " + displayLimit + ")"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } } public Object visit(ASTrand_input_statement node, Object data) { // 0 for input projection rand_common(node, 0, data); return data; } public Object visit(ASTrand_output_statement node, Object data) { // 1 for output projection rand_common(node, 1, data); return data; } /* * See rand_common above, can't currently handle both projections with ListAllStrings() public Object visit(ASTrand_output_statement node, Object data) { // 2 for both projections rand_common(node, 2, data) ; return data ; } */ public Object visit(ASTnum_list_func_call node, Object data) { // Usual Syntax: #@^__tail(#@arr) has a NumList value // // num_list_func_call always has two daughters: // num_list_func_exp // arg_list // // N.B. num_list_func_exp could be // num_list_func_id e.g. #@^myfunc OR // num_list_func_anon_exp e.g. #^(...){...} // so num_list_func_exp needs to be evaluated // Evaluate daughter 0, the num_list_func_exp node.jjtGetChild(0).jjtAccept(this, data); // Should leave a FuncValue on the stack FuncValue funcValue = (FuncValue) stack.pop(); // now evaluate the arg_list, which can have 0 to 2 daughters // arg_list // positional_args // named_args // If either daughter is present, it is non-empty. // If both daughters are present, // positional_args is always before named_args // N.B. the arguments have to be evaluated in the current Frame, // before allocating a new daughter frame for the execution of the // function body. // Evaluate daughter 1, the arg_list node.jjtGetChild(1).jjtAccept(this, data); // should leave on the stack, from top down // 1. an ArgCounts object (containing positional_args_count // and named_args_count) // 2. the positional arguments (in syntactic order), // the number being positional_args_count // 3. the named arguments (each represented by a NamedArg object), // the number being named_args_count // // first pop off the ArgCounts object, // leaving the evaluated args (if any) on the stack ArgCounts ac = (ArgCounts) stack.pop(); // Now allocate a new Frame for execution of this function call // (N.B. released below when the function returns) env.allocateFrame(funcValue.getStaticFrame()); // now bind the formal params to the arg values, in the new Frame try { bind_params(funcValue.getParamArrayList(), ac, data); // may throw FuncCallException, a kind of RuntimeException // catch all Exceptions and release the frame before rethrowing } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // now execute the body of the function (a func_block); more precisely, // send a message to the function block telling it to accept this // IntepreterVisitor try { funcValue.getFuncBlock().jjtAccept(this, data); } catch (RuntimeException re) { env.releaseFrame(); throw re; } // catch (Exception e) { // env.releaseFrame() ; // throw e ; //} // normal release of the Frame created for the execution of this func call env.releaseFrame(); // Check return value; // there should be a NumList object left on the stack // (this is ASTnum_list_func_call) by a return stmt in the funcBlock. Object obj = stack.peek(); if (obj == null) { throw new FuncCallException("Number list valued function call failed to return a number list."); } else if (!(obj instanceof NumList)) { throw new FuncCallException("Number list valued function call returns incorrect type."); } return data; } public Object visit(ASTnum_list_func_exp node, Object data) { node.jjtGetChild(0).jjtAccept(this, data); return data; } public Object visit(ASTnum_list_func_id node, Object data) { // this is called for num_list_func_id on the right-hand-side, // so it needs to successfully look up the FuncValue value // (or throw // an exception); for an assignment, e.g. #@^func = // this visit method is not called on the LHS String num_list_func_id = node.getImage(); // value from environment should be a FuncValue object Object obj = env.get(num_list_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined num_list_func_id: " + num_list_func_id); } return data; } public Object visit(ASTnum_list_func_func_id node, Object data) { // this is called for num_list_func_func_id on the right-hand-side, // so it needs to successfully look up the FuncValue value // (or throw // an exception); for an assignment, e.g. #@^func = // this visit method is not called on the LHS String num_list_func_func_id = node.getImage(); // value from environment should be a FuncValue object Object obj = env.get(num_list_func_func_id); if (obj != null) { stack.push(obj); } else { throw new UndefinedIdException("Undefined num_list_func_func_id: " + num_list_func_func_id); } return data; } public Object visit(ASTnum_list_func_anon_exp node, Object data) { // two daughters: param_list, func_block // collect them into a new FuncValue object // and push it on the stack // Don't evaluate the func_block, just get the AST itself node.jjtGetChild(0).jjtAccept(this, data); ArrayList<ParamSlot> pal = (ArrayList<ParamSlot>) stack.pop(); // a FuncValue also needs to store a handle to the frame // in which it was defined (to get lexical scoping) stack.push(new FuncValue(env.getCurrentFrame(), pal, (ASTfunc_block) node.jjtGetChild(1))); return data; } public Object visit(ASTany node, Object data) { // syntax is just . (dot) Fst anyFst = lib.SigmaFst(); // The # symbol used in rules should never be matched by . (any) KRB ruleany // Rethink: 2015-01-18 the OTHER in a _rule_ FST should not match # //anyFst.getSigma().add(symmap.putsym(hulden.ruleWordBoundarySym)) ; stack.push(anyFst); return data; } public Object visit(ASTany_any node, Object data) { // syntax is just .:. (perhaps with whitespace), tokenized // as a single token (may contain whitespace) Fst anyAnyFst = lib.SigmaSigmaFst(); // The # symbol used in rules should never be matches by .:. KRB ruleany // Rethink: 2015-01-18 the OTHER in a _rule_ FST should not match # //anyAnyFst.getSigma().add(symmap.putsym(hulden.ruleWordBoundarySym)) ; stack.push(anyAnyFst); return data; } public Object visit(ASTepsilon node, Object data) { // syntax is just U+03F5 or _e_ // denotes the empty string language stack.push(lib.EmptyStringLanguageFst()); return data; } public Object visit(ASTarg_list node, Object data) { // any positional arguments must appear before any named // arguments (syntactically constrained) // // arg_list 0 to 2 daughters // positional_args // XXX_exp // XXX_exp // ... // named_args // XXX_with_assignment // XXX_with_assignment // ... // // if either _args daughter is there, it is non-empty int positional_args_count = 0; int named_args_count = 0; int childCount = node.jjtGetNumChildren(); if (childCount == 2) { // then there are both positional and named args; // N.B. DO THE NAMED ARGS FIRST!! All arg values will be // pushed on the stack in the reverse of their syntactic order, // so that they can be popped off in the original // syntactic order. // evaluate the named_args first (child 1) node.jjtGetChild(1).jjtAccept(this, data); // leaves an Integer object on top of the stack named_args_count = ((Integer) stack.pop()).intValue(); // and it leaves the names-values on the stack in // NamedArg objects // then evaluate the positional_args (child 0) node.jjtGetChild(0).jjtAccept(this, data); // leaves an Integer object on top of the stack positional_args_count = ((Integer) stack.pop()).intValue(); // and it leaves the arg value objects on the stack } else if (childCount == 1) { // then there are positional XOR named args Object obj = node.jjtGetChild(0); if (obj instanceof ASTpositional_args) { node.jjtGetChild(0).jjtAccept(this, data); positional_args_count = ((Integer) stack.pop()).intValue(); } else { node.jjtGetChild(0).jjtAccept(this, data); named_args_count = ((Integer) stack.pop()).intValue(); } } // leave on top of the stack an ArgCounts object that records the // number of positional_args and the number of named_args stack.push(new ArgCounts(positional_args_count, named_args_count)); return data; } public Object visit(ASTpositional_args node, Object data) { int positional_args_count = node.jjtGetNumChildren(); // The daughters are various kinds of _exp // // loop backwards through the positional args, pushing the values // on the stack (so that they can be popped off in syntactic order) for (int i = positional_args_count - 1; i >= 0; i--) { node.jjtGetChild(i).jjtAccept(this, data); // leaves a value object (FST, Long, Double, whatever...) // on the stack } // leave on top of the stack the count stack.push(new Integer(positional_args_count)); return data; } public Object visit(ASTnamed_args node, Object data) { int named_args_count = node.jjtGetNumChildren(); // each daughter is an XXX_with_assignment // // loop backwards through the named args, pushing NamedArg // objects on the stack // (so that they can be popped off in the original syntactic order) // --IN FACT, I DON'T THINK THE RELATIVE ORDER OF NAMED _ARGS_ // IS SIGNIFICANT, but I'm // pushing all the args on the stack in reverse syntactic order for (int i = named_args_count - 1; i >= 0; i--) { ((InterpData) data).setIsArg(true); node.jjtGetChild(i).jjtAccept(this, data); // should leave a NamedArg object on the stack } // add on top of the stack the count stack.push(new Integer(named_args_count)); return data; } public Object visit(ASTparam_list node, Object data) { // param_list 0 to 2 daughters // required_params (names, no default value indicated) // optional_params (names with default values indicated) // // both the required_params and optional_params are optional // (so param_list may be empty) // // when a function is defined, it may have required_params // and/or optional_params // (the optional params have default values indicated). // The default values, // if any, need to be computed and stored at define time (in an // ArrayList<ParamSlot>). ArrayList<ParamSlot> pal = new ArrayList<ParamSlot>(); // this pal will be pushed on the stack // will consist of ParamSlot objects, in syntactic order // initially assume no params at all ASTrequired_params required_params = null; ASToptional_params optional_params = null; int paramListChildCount = node.jjtGetNumChildren(); if (paramListChildCount == 2) { // then there is an ASTrequired_params, followed by // an ASToptional_params required_params = (ASTrequired_params) node.jjtGetChild(0); optional_params = (ASToptional_params) node.jjtGetChild(1); } else if (paramListChildCount == 1) { Object obj = node.jjtGetChild(0); if (obj instanceof ASTrequired_params) { required_params = (ASTrequired_params) obj; } else { optional_params = (ASToptional_params) obj; } } // required params do not have default values if (required_params != null) { int required_param_count = required_params.jjtGetNumChildren(); for (int i = 0; i < required_param_count; i++) { // process the params in syntactic order Object p = required_params.jjtGetChild(i); // need to get the image and store it in a ParamSlot String image = ""; if (p instanceof ASTnet_id) { image = ((ASTnet_id) p).getImage(); } else if (p instanceof ASTnet_func_id) { image = ((ASTnet_func_id) p).getImage(); } else if (p instanceof ASTnet_func_func_id) { image = ((ASTnet_func_func_id) p).getImage(); } else if (p instanceof ASTnet_list_id) { image = ((ASTnet_list_id) p).getImage(); } else if (p instanceof ASTnet_list_func_id) { image = ((ASTnet_list_func_id) p).getImage(); } else if (p instanceof ASTnet_list_func_func_id) { image = ((ASTnet_list_func_func_id) p).getImage(); } else if (p instanceof ASTnum_id) { image = ((ASTnum_id) p).getImage(); } else if (p instanceof ASTnum_func_id) { image = ((ASTnum_func_id) p).getImage(); } else if (p instanceof ASTnum_func_func_id) { image = ((ASTnum_func_func_id) p).getImage(); } else if (p instanceof ASTnum_list_id) { image = ((ASTnum_list_id) p).getImage(); } else if (p instanceof ASTnum_list_func_id) { image = ((ASTnum_list_func_id) p).getImage(); } else if (p instanceof ASTnum_list_func_func_id) { image = ((ASTnum_list_func_func_id) p).getImage(); } else { throw new UndefinedIdException( "Error checking required params. See interpreter ASTparam_list"); } // add a ParamSlot to the pal (name, but no default value) pal.add(new ParamSlot(image)); } } if (optional_params != null) { // optional params in function definitions have default // values indicated int optional_param_count = optional_params.jjtGetNumChildren(); for (int i = 0; i < optional_param_count; i++) { // Optional params are much like assignment statements; // need to get the assigned ID name (the LHS) as a String, // without actually evaluating it. // Each daughter of optional params has the form // // XXX_with_assignment // XXX child 0 // an expression of that type child 1 // // Each pair (id-value) will be pushed on the stack // as a ParamSlot object ((InterpData) data).setIsArg(false); // arg vs. param, this is a parameter optional_params.jjtGetChild(i).jjtAccept(this, data); // the "param" (not arg) setting tells the method to push a // ParamSlot rather than a NamedArg // add the new ParamSlot to the pal, with the default value pal.add((ParamSlot) stack.pop()); } } stack.push(pal); return data; } public Object visit(ASTrequired_params node, Object data) { // not called directly // see ASTparam_list return data; } public Object visit(ASToptional_params node, Object data) { // not called directly // see ASTparam_list return data; } // the following with_assignment nodes found both in // param_list/optional_params and in arg_list/named_args ; // Called with data being InterpData, as usual, with // .getIsArg() either true (an argument) or false (a parameter) // for .getIsArg() == true push a NamedArg object // for .getIsArg() == false push a ParamSlot object public Object visit(ASTnet_id_with_assignment node, Object data) { String image = ((ASTnet_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTnet_func_id_with_assignment node, Object data) { String image = ((ASTnet_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTvoid_func_id_with_assignment node, Object data) { String image = ((ASTvoid_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTnet_func_func_id_with_assignment node, Object data) { String image = ((ASTnet_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTvoid_func_func_id_with_assignment node, Object data) { String image = ((ASTvoid_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTnet_list_id_with_assignment node, Object data) { String image = ((ASTnet_list_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTnet_list_func_id_with_assignment node, Object data) { String image = ((ASTnet_list_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } //public Object visit(ASTnet_list_func_func_id_with_assignment node, Object data) { // String image = ((ASTnet_list_func_func_id)node.jjtGetChild(0)).getImage() ; // node.jjtGetChild(1).jjtAccept(this, data) ; // if (((InterpData)data).getIsArg()) { // stack.push(new NamedArg(image, stack.pop()) ; // } else { // stack.push(new ParamSlot(image, stack.pop()) ; // } // return data ; //} public Object visit(ASTnum_id_with_assignment node, Object data) { String image = ((ASTnum_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTnum_func_id_with_assignment node, Object data) { String image = ((ASTnum_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTnum_func_func_id_with_assignment node, Object data) { String image = ((ASTnum_func_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTnum_list_id_with_assignment node, Object data) { String image = ((ASTnum_list_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } public Object visit(ASTnum_list_func_id_with_assignment node, Object data) { String image = ((ASTnum_list_func_id) node.jjtGetChild(0)).getImage(); node.jjtGetChild(1).jjtAccept(this, data); if (((InterpData) data).getIsArg()) { stack.push(new NamedArg(image, stack.pop())); } else { stack.push(new ParamSlot(image, stack.pop())); } return data; } //public Object visit(ASTnum_list_func_func_id_with_assignment node, Object data) { // String image = ((ASTnum_list_func_func_id)node.jjtGetChild(0)).getImage() ; // node.jjtGetChild(1).jjtAccept(this, data) ; // if (((InterpData)data).getIsArg()) { // stack.push(new NamedArg(image, stack.pop()) ; // } else { // stack.push(new ParamSlot(image, stack.pop()) ; // } // return data ; //} public Object visit(ASTexternal_statement node, Object data) { // one or more children, of various _id types int numChildren = node.jjtGetNumChildren(); String img = null; for (int i = 0; i < numChildren; i++) { Object idobj = node.jjtGetChild(i); if (idobj instanceof ASTnet_id) { img = ((ASTnet_id) idobj).getImage(); } else if (idobj instanceof ASTrrprod_id) { img = ((ASTrrprod_id) idobj).getImage(); } else if (idobj instanceof ASTnet_func_id) { img = ((ASTnet_func_id) idobj).getImage(); } else if (idobj instanceof ASTnet_func_func_id) { img = ((ASTnet_func_func_id) idobj).getImage(); } else if (idobj instanceof ASTnet_list_id) { img = ((ASTnet_list_id) idobj).getImage(); } else if (idobj instanceof ASTnet_list_func_id) { img = ((ASTnet_list_func_id) idobj).getImage(); } else if (idobj instanceof ASTnet_list_func_func_id) { img = ((ASTnet_list_func_func_id) idobj).getImage(); } else if (idobj instanceof ASTnum_id) { img = ((ASTnum_id) idobj).getImage(); } else if (idobj instanceof ASTnum_func_id) { img = ((ASTnum_func_id) idobj).getImage(); } else if (idobj instanceof ASTnum_func_func_id) { img = ((ASTnum_func_func_id) idobj).getImage(); } else if (idobj instanceof ASTnum_list_id) { img = ((ASTnum_list_id) idobj).getImage(); } else if (idobj instanceof ASTnum_list_func_id) { img = ((ASTnum_list_func_id) idobj).getImage(); } else if (idobj instanceof ASTnum_list_func_func_id) { img = ((ASTnum_list_func_func_id) idobj).getImage(); } else if (idobj instanceof ASTvoid_func_id) { img = ((ASTvoid_func_id) idobj).getImage(); } else if (idobj instanceof ASTvoid_func_func_id) { img = ((ASTvoid_func_func_id) idobj).getImage(); } // bind the img to an ExternValue object // in the current frame env.markExternalInCurrentFrame(img); } return data; } public Object visit(ASTexport_statement node, Object data) { // one or more children, of various _id types int numChildren = node.jjtGetNumChildren(); String img = null; String icon = null; for (int i = 0; i < numChildren; i++) { Object idobj = node.jjtGetChild(i); if (idobj instanceof ASTnet_id) { img = ((ASTnet_id) idobj).getImage(); icon = SymtabIcons.NET_IMAGE; } else if (idobj instanceof ASTrrprod_id) { img = ((ASTrrprod_id) idobj).getImage(); icon = SymtabIcons.RRPROD_IMAGE; } else if (idobj instanceof ASTnet_func_id) { img = ((ASTnet_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; } else if (idobj instanceof ASTnet_func_func_id) { img = ((ASTnet_func_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; } else if (idobj instanceof ASTnet_list_id) { img = ((ASTnet_list_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; // KRB fix } else if (idobj instanceof ASTnet_list_func_id) { img = ((ASTnet_list_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; // KRB fix } else if (idobj instanceof ASTnet_list_func_func_id) { img = ((ASTnet_list_func_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; // KRB fix } else if (idobj instanceof ASTnum_id) { img = ((ASTnum_id) idobj).getImage(); icon = SymtabIcons.NUM_IMAGE; } else if (idobj instanceof ASTnum_func_id) { img = ((ASTnum_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; } else if (idobj instanceof ASTnum_func_func_id) { img = ((ASTnum_func_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; } else if (idobj instanceof ASTnum_list_id) { img = ((ASTnum_list_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; // KRB fix } else if (idobj instanceof ASTnum_list_func_id) { img = ((ASTnum_list_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; // KRB fix } else if (idobj instanceof ASTnum_list_func_func_id) { img = ((ASTnum_list_func_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; // KRB fix } else if (idobj instanceof ASTvoid_func_id) { img = ((ASTvoid_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; } else if (idobj instanceof ASTvoid_func_func_id) { img = ((ASTvoid_func_id) idobj).getImage(); icon = SymtabIcons.FUNC_IMAGE; } // Find the value bound to img in the currentFrame // and "export" that name-value binding to the // dynamicMother Frame env.exportToDynamicMotherFrame(img); if (((InterpData) data).getInGUI() == true && env.getCurrentFrame().getDynamicMother() == mainFrame) { // add an Icon to the Symtab window addToGUISymtab(img, icon, data); } } return data; } public Object visit(ASTdelete_statement node, Object data) { // one or more children, of various _id types int numChildren = node.jjtGetNumChildren(); String img = null; for (int i = 0; i < numChildren; i++) { Object idobj = node.jjtGetChild(i); if (idobj instanceof ASTnet_id) { // DON'T TRY TO DELETE THE NATIVE OBJECT HERE; // THERE MAY BE MULTIPLE IDs // REFERRING TO THE SAME NATIVE FST img = ((ASTnet_id) idobj).getImage(); } else if (idobj instanceof ASTrrprod_id) { img = ((ASTrrprod_id) idobj).getImage(); } else if (idobj instanceof ASTnet_func_id) { img = ((ASTnet_func_id) idobj).getImage(); } else if (idobj instanceof ASTnet_func_func_id) { img = ((ASTnet_func_func_id) idobj).getImage(); } else if (idobj instanceof ASTnet_list_id) { img = ((ASTnet_list_id) idobj).getImage(); } else if (idobj instanceof ASTnet_list_func_id) { img = ((ASTnet_list_func_id) idobj).getImage(); //} else if (idobj instanceof ASTnet_list_func_func_id) { // img = ((ASTnet_list_func_func_id)idobj).getImage() ; } else if (idobj instanceof ASTnum_id) { img = ((ASTnum_id) idobj).getImage(); } else if (idobj instanceof ASTnum_func_id) { img = ((ASTnum_func_id) idobj).getImage(); } else if (idobj instanceof ASTnum_func_func_id) { img = ((ASTnum_func_func_id) idobj).getImage(); } else if (idobj instanceof ASTnum_list_id) { img = ((ASTnum_list_id) idobj).getImage(); } else if (idobj instanceof ASTnum_list_func_id) { img = ((ASTnum_list_func_id) idobj).getImage(); } else if (idobj instanceof ASTnum_list_func_func_id) { img = ((ASTnum_list_func_func_id) idobj).getImage(); } else if (idobj instanceof ASTvoid_func_id) { img = ((ASTvoid_func_id) idobj).getImage(); } else if (idobj instanceof ASTvoid_func_func_id) { img = ((ASTvoid_func_func_id) idobj).getImage(); } Frame foundFrame = null; foundFrame = env.remove(img); // frame where img was // found and removed if (foundFrame == mainFrame) { // Returns the Frame where a real key-object entry was // removed, else null // if the GUI is active, remove the icon from the // GUI symtab window final String fimg = img; final Object fdata = data; if (((InterpData) data).getInGUI()) { if (javax.swing.SwingUtilities.isEventDispatchThread()) { removeFromGUISymtab(fimg, fdata); } else { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { removeFromGUISymtab(fimg, fdata); } }); } } } } return data; } public Object visit(ASTdelete_all_statement node, Object data) { // no children // find all networks, productions, etc. defined in // currentFrame and delete them Frame currentFrame = env.getCurrentFrame(); HashSet<String> keySet = currentFrame.keySet(); boolean inGUI = false; PseudoTerminalInternalFrame terminal = null; if (((InterpData) data).getInGUI() == true) { inGUI = true; terminal = ((InterpData) data).getGUI().getTerminal(); } for (Iterator<String> i = keySet.iterator(); i.hasNext();) { String key = i.next(); Object obj = currentFrame.get(key); // don't try to delete entries where the value is // a FreeVariable, indicating that the key would // looked up in this scope as a free variable. if (!(obj instanceof FreeVariable)) { Frame foundFrame = null; foundFrame = env.remove(key); final String fkey = key; final Object fdata = data; if (foundFrame == mainFrame) { if (inGUI) { if (javax.swing.SwingUtilities.isEventDispatchThread()) { removeFromGUISymtab(fkey, fdata); } else { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { removeFromGUISymtab(fkey, fdata); } }); } } } if (inGUI) { final PseudoTerminalInternalFrame fterminal = terminal; if (javax.swing.SwingUtilities.isEventDispatchThread()) { fterminal.appendToHistory("// Deleting " + fkey); } else { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { fterminal.appendToHistory("// Deleting " + fkey); } }); } } } } return data; } public Object visit(ASToptimize_statement node, Object data) { // syntax: optimize $foo, $bar ... ; // or optimize! $foo, $bar ... ; // (optimization is always done in place) // one or more children, all of type net_id(), syntactically constrained int numChildren = node.jjtGetNumChildren(); for (int i = 0; i < numChildren; i++) { node.jjtGetChild(i).jjtAccept(this, data); // look up the net_id, leaves handle to an Fst object on the stack Fst fst = (Fst) stack.pop(); // force the optimization, even if user has set // default optimization (or parts: determinize, minimize, // rmepsilon) to false lib.OptimizeInPlaceForce(fst); } return data; } private void runGC(boolean inGUI, PseudoTerminalInternalFrame terminal) { Runtime runtime = Runtime.getRuntime(); int gcIterations = 12; // KRB: magic number for (int i = 0; i < gcIterations; i++) { if (inGUI) { terminal.appendToHistory("// Outer Iteration"); terminal.appendToHistory("// Before After"); } else { System.out.println("// Outer Iteration"); System.out.println("// Before After"); } _runGC(inGUI, terminal, runtime); } } private long getMemInUse(Runtime runtime) { return runtime.totalMemory() - runtime.freeMemory(); } private void _runGC(boolean inGUI, PseudoTerminalInternalFrame terminal, Runtime runtime) { long memInUseAfter = getMemInUse(runtime), memInUseBefore = Long.MAX_VALUE; for (int j = 0; (memInUseAfter < memInUseBefore) && (j < 10); // KRB: magic number j++) { runtime.runFinalization(); runtime.gc(); Thread.currentThread().yield(); memInUseBefore = memInUseAfter; memInUseAfter = getMemInUse(runtime); if (inGUI) { terminal.appendToHistory(memInUseBefore + " " + memInUseAfter); } else { System.out.println(memInUseBefore + " " + memInUseAfter); } } } public Object visit(ASTgarbage_collect_statement node, Object data) { boolean inGUI = ((InterpData) data).getInGUI(); PseudoTerminalInternalFrame terminal = null; if (inGUI) { terminal = ((InterpData) data).getGUI().getTerminal(); terminal.appendToHistory("// Suggesting Java garbage collection"); } runGC(inGUI, terminal); return data; } public Object visit(ASTmemory_report_statement node, Object data) { Runtime r = Runtime.getRuntime(); long max = r.maxMemory(); long total = r.totalMemory(); long free = r.freeMemory(); long inuse = total - free; if (((InterpData) data).getInGUI() == true) { PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); terminal.appendToHistory("// " + max + " max memory that Java will try to use"); terminal.appendToHistory("// " + total + " total memory"); terminal.appendToHistory("// " + free + " free"); terminal.appendToHistory("// " + inuse + " in use (total - free)"); } else { System.out.println("// " + max + " max memory that Java will try to use"); System.out.println("// " + total + " total memory"); System.out.println("// " + free + " free memory"); System.out.println("// " + inuse + " memory in use (total - free)"); } return data; } public Object visit(ASTfsts_report_statement node, Object data) { int fstsAllocated = Fst.getCountOfFstsAllocated(); int callsToFinalize = Fst.getCountOfCallsToFinalize(); int fstsFinalized = Fst.getCountOfFstsFinalized(); int fstsOpen = fstsAllocated - fstsFinalized; if (((InterpData) data).getInGUI() == true) { PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); terminal.appendToHistory("// " + fstsAllocated + " allocated"); terminal.appendToHistory("// " + callsToFinalize + " calls to finalize()"); terminal.appendToHistory("// " + fstsFinalized + " finalized"); terminal.appendToHistory("// " + fstsOpen + " open"); } else { System.out.println("// " + fstsAllocated + " allocated"); System.out.println("// " + callsToFinalize + " calls to finalize()"); System.out.println("// " + fstsFinalized + " finalized"); System.out.println("// " + fstsOpen + " open"); } return data; } public Object visit(ASTsymtab_report_statement node, Object data) { HashSet<String> hs = mainFrame.keySet(); Iterator<String> i = hs.iterator(); PseudoTerminalInternalFrame terminal = null; if (((InterpData) data).getInGUI() == true) { terminal = ((InterpData) data).getGUI().getTerminal(); } while (i.hasNext()) { String id = i.next(); if (terminal != null) { terminal.appendToHistory("// " + id); } else { System.out.println("// " + id); } } return data; } public Object visit(ASTgsymtab_report_statement node, Object data) { HashSet<String> hs = mainFrame.getStaticMother().keySet(); Iterator<String> i = hs.iterator(); PseudoTerminalInternalFrame terminal = null; if (((InterpData) data).getInGUI() == true) { terminal = ((InterpData) data).getGUI().getTerminal(); } while (i.hasNext()) { String id = i.next(); if (terminal != null) { terminal.appendToHistory("// " + id); } else { System.out.println("// " + id); } } return data; } // see also ASTnet_rmepsilon_func_call public Object visit(ASTrmEpsilon_statement node, Object data) { // one or more children, all net_id()s // syntax: rmEpsilon regex [(comma)? regex]* ; // These rmEpsilon operations are to be done In Place int numChildren = node.jjtGetNumChildren(); for (int i = 0; i < numChildren; i++) { node.jjtGetChild(i).jjtAccept(this, data); Fst fst = (Fst) stack.pop(); lib.RmEpsilonInPlace(fst); } return data; } // see also ASTnet_determinize_func_call // BOTH ARE DANGEROUS -- could be called on networks that are // not determinizable public Object visit(ASTdeterminize_statement node, Object data) { // one or more children, net_id()s // syntax: determinize regex [(comma)? regex]* ; // or determinize! ... // typically: determinize $net ; // These determinize operations are to be done In Place, so that any // other aliases to $net also point to the determinized result. int numChildren = node.jjtGetNumChildren(); for (int i = 0; i < numChildren; i++) { node.jjtGetChild(i).jjtAccept(this, data); Fst fst = (Fst) stack.pop(); lib.DeterminizeInPlace(fst); } return data; } // see also ASTnet_minimize_func_call public Object visit(ASTminimize_statement node, Object data) { // one or more children, net_id()s // syntax: minimize regex [(comma)? regex]* ; // These minimize operations are to be done In Place int numChildren = node.jjtGetNumChildren(); boolean inGUI = false; PseudoTerminalInternalFrame terminal = null; if (((InterpData) data).getInGUI() == true) { inGUI = true; terminal = ((InterpData) data).getGUI().getTerminal(); } for (int i = 0; i < numChildren; i++) { ASTnet_id obj = (ASTnet_id) node.jjtGetChild(i); String img = obj.getImage(); obj.jjtAccept(this, data); Fst fst = (Fst) stack.pop(); if (!lib.IsIDeterministic(fst)) { String warningMsg = "// WARNING: The network argument to be minimized, " + img + ", must first be determinized."; if (inGUI) { terminal.appendToHistory(warningMsg); } else { System.out.println(warningMsg); } } else { // the network is deterministic, so it can be minimized lib.MinimizeInPlace(fst); } } return data; } public Object visit(ASTsynchronize_statement node, Object data) { // one or more children, net_id()s // syntax: synchronize regex [(comma)? regex]* ; // or synchronize! ... // typically: synchronize $net ; // These synchronize operations are to be done In Place, so that any // other aliases to $net also point to the synchronized result. int numChildren = node.jjtGetNumChildren(); for (int i = 0; i < numChildren; i++) { node.jjtGetChild(i).jjtAccept(this, data); Fst fst = (Fst) stack.pop(); lib.SynchronizeInPlace(fst); } return data; } public Object visit(ASTinfo_statement node, Object data) { // one daughter: some kind of expression, (output to the GUI) // or two: some kind of expression, filepath // (output to file, default encoding of // the OS) // or three: some kind of expression, filepath, encoding // (output to file, user-specified // encoding) int numChildren = node.jjtGetNumChildren(); String filePath = ""; String encoding = "-"; // OS default if (numChildren >= 2) { // set the filepath for the output // Child #1 should indicate the file name node.jjtGetChild(1).jjtAccept(this, data); Fst filePathFst = (Fst) (stack.pop()); filePath = lib.GetSingleString(filePathFst, "Second arg to info (filepath) must denote a language of exactly one string."); if (filePath.length() == 0) { throw new KleeneArgException( "Second arg to info (filepath) must denote a language of exactly one non-empty string"); } } if (numChildren == 3) { // set the encoding // Child #2 should specify the encoding node.jjtGetChild(2).jjtAccept(this, data); Fst encodingFst = (Fst) (stack.pop()); encoding = lib.GetSingleString(encodingFst, "Third arg to info (encoding) must denote a language of exactly one string."); if (encoding.length() == 0) { throw new KleeneArgException("Third arg to info (encoding) must denote a non-empty string"); } } InfoWriter infoWriter = null; // InfoWriter Java class knows how to write output, // takes care of the encoding if (numChildren >= 2) { String fullpath = getFullpath(filePath); if (encoding.equals("-")) { // get current OS encoding encoding = System.getProperty("file.encoding"); } infoWriter = new InfoWriter(new File(fullpath), encoding); } // if the output is to file, or we are in the GUI (i.e. just one daughter) // (else it's in a script, with no output file indicated, // and output makes no sense) boolean inGUI = ((InterpData) data).getInGUI(); // Get the object for which info is desired node.jjtGetChild(0).jjtAccept(this, data); // Should leave object on the stack Object obj = stack.pop(); PseudoTerminalInternalFrame terminal = null; if (numChildren == 1 && inGUI) { // output to the GUI terminal terminal = ((InterpData) data).getGUI().getTerminal(); } String infoString = ""; if (obj instanceof Fst) { Fst fst = (Fst) obj; // see Java func basicFstInfo() above infoString = "// " + basicFstInfo(fst); } else if (obj instanceof Long) { infoString = "// Long value: " + ((Long) obj).longValue(); } else if (obj instanceof Double) { infoString = "// Double value: " + ((Double) obj).doubleValue(); } else if (obj instanceof NetList) { infoString = "// Network list value: " + "Size: " + ((NetList) obj).size(); } else if (obj instanceof NumList) { infoString = "// Number list value: " + "Size: " + ((NumList) obj).size(); } else { infoString = "// No info display implemented yet for this datatype."; } if (numChildren == 1) { if (inGUI) { terminal.appendToHistory(infoString); } else { System.out.println(infoString); } } else { infoWriter.writeLine(infoString); } if (infoWriter != null) { infoWriter.close(); } return data; } public Object visit(ASTtest_statement node, Object data) { // syntax: test $foo ; // test a*b+[d-g]? ; // test $foo, "$foo" ; // the first required daughter, any ASTregexp, is the // network to be tested // test makes no sense outside the GUI if (((InterpData) data).getInGUI()) { // evaluate the zeroth child (the regexp) node.jjtGetChild(0).jjtAccept(this, data); // should leave an Fst object on the stack Fst fst = (Fst) (stack.pop()); // the second daughter, the test-window title, is optional String title; if (node.jjtGetNumChildren() == 2) { // then a title was specified for the test window node.jjtGetChild(1).jjtAccept(this, data); Fst pathFst = (Fst) (stack.pop()); title = lib.GetSingleString(pathFst, "Second arg to test must denote a language of exactly one string."); } else { title = "Anonymous Fst"; } // TranslitTokenizerBuilder is a class that knows how to make // ICU4J Transliterators to tokenize a raw input string // (including finding user-defined multichar symbols); // In the Xerox/PARC tradition, separate Transliterators are // made for the upper ("input" for OpenFst) and // lower ("output" for OpenFst) sides TranslitTokenizerBuilder ttb = new TranslitTokenizerBuilder(symmap, fst.getSigma(), lib); // Iterate4mcs (knows how to iterate // through the Fst) // Calls back to .registerMcsInput() or // .registerMcsOutput() method // in the TranslitTokenizerBuilder ttb for each new // Multichar Symbol found on the input or output side, // respectively. It needs to know the SymMap's // .getStartPuaCpv() so that it knows what a multichar // symbol is (in the network being visited, the labels // are just integers) lib.Iterate4mcs(fst, ttb, symmap.getStartPuaCpv()); boolean inputSide = true; boolean outputSide = false; // used to tokenize string input for generation // (matched against the upper/"input" side) // 'true' arg means to consider only the multi-char // symbols on the Input Side Transliterator trInput = ttb.getTranslitTokenizer(inputSide); // used to tokenize string input for analysis (matched against // the lower/"output" side) // 'false' arg means to consider only multi-char symbols // on the Output Side Transliterator trOutput = ttb.getTranslitTokenizer(outputSide); // display special JInternalFrame where the user can type // in input for testing TestFstInternalFrame tfif = new TestFstInternalFrame(title, env, trInput, trOutput, symmap, fst, this, data); KleeneGUI g = ((InterpData) data).getGUI(); g.showTestFstInternalFrame(tfif); tfif.moveToFront(); } return data; } public Object visit(ASTassert_statement node, Object data) { // daughters: // numexp() // boolean, required // regexp() // optional, should denote a single-string language // evaluate the numexp(), interpret as boolean (true or false) // if not true, then throw an AssertException node.jjtGetChild(0).jjtAccept(this, data); if (!lib.isTrue(stack.pop())) { String msg = ""; if (node.jjtGetNumChildren() == 2) { node.jjtGetChild(1).jjtAccept(this, data); Fst msgFst = (Fst) (stack.pop()); msg = lib.GetSingleString(msgFst, "Second arg to assert() must denote a language of exactly one string"); } throw new AssertException(msg); } return data; } public Object visit(ASTrequire_statement node, Object data) { // daughters: // numexp() // boolean, required // regexp() // optional, should denote a single-string language // evaluate the numexp(), interpret as boolean (true or false) // if not true, then throw an AssertException node.jjtGetChild(0).jjtAccept(this, data); if (!lib.isTrue(stack.pop())) { String msg = ""; if (node.jjtGetNumChildren() == 2) { node.jjtGetChild(1).jjtAccept(this, data); Fst msgFst = (Fst) (stack.pop()); msg = lib.GetSingleString(msgFst, "Second arg to require() must denote a language of exactly one string"); } throw new RequireException(msg); } return data; } public Object visit(ASTprint_statement node, Object data) { // the first required daughter, an ASTregexp; this is the // net to list // second optional daughter is the separator to print after // each string node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); if (!lib.IsAcceptor(fst)) { throw new KleeneArgException("First arg to print must denote a language, not a relation."); } String sepString = "\n"; // default newline separator if (node.jjtGetNumChildren() == 2) { node.jjtGetChild(1).jjtAccept(this, data); Fst sepFst = (Fst) (stack.pop()); sepString = lib.GetSingleString(sepFst, "Second arg to print must denote a language of exactly one string."); } int displayLimit = 100; long stringCount = lib.NumPaths(fst); PseudoTerminalInternalFrame terminal = null; if (((InterpData) data).getInGUI()) { terminal = ((InterpData) data).getGUI().getTerminal(); } if (stringCount == 0) { String msg = "(language is empty)"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } else if (stringCount == -1) { // then has loops, infinite language String msg = "(language is infinite)"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } else if (stringCount <= displayLimit) { // then just list them (parameterize this figure later) if (terminal != null) { FstStringLister lister = new FstStringLister(terminal, symmap); // native function; second arg 0 is for input side, 1 for output side lib.ListAllStrings(fst, 0, lister); } else { FstSystemStringLister sysLister = new FstSystemStringLister(symmap, sepString); lib.ListAllStrings(fst, 0, sysLister); } } else { String msg = "(language exceeds displayLimit: " + displayLimit + ")"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } return data; } // pr_statement doesn't print out the weights public Object visit(ASTpr_statement node, Object data) { // the first required daughter, an ASTregexp; this is the // net to list // second optional daughter is the separator to print after // each string node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); if (!lib.IsAcceptor(fst)) { throw new KleeneArgException("First arg to print must denote a language, not a relation."); } String sepString = "\n"; // default newline separator if (node.jjtGetNumChildren() == 2) { node.jjtGetChild(1).jjtAccept(this, data); Fst sepFst = (Fst) (stack.pop()); sepString = lib.GetSingleString(sepFst, "Second arg to print must denote a language of exactly one string."); } int displayLimit = 100; long stringCount = lib.NumPaths(fst); PseudoTerminalInternalFrame terminal = null; if (((InterpData) data).getInGUI()) { terminal = ((InterpData) data).getGUI().getTerminal(); } if (stringCount == 0) { String msg = "(language is empty)"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } else if (stringCount == -1) { // then has loops, infinite language String msg = "(language is infinite)"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } else if (stringCount <= displayLimit) { // then just list them (parameterize this figure later) if (terminal != null) { FstStringLister lister = new FstStringLister(terminal, symmap); // native function; second arg 0 is for input side, 1 for output side lib.ListAllStringsNoWeight(fst, 0, lister); } else { FstSystemStringLister sysLister = new FstSystemStringLister(symmap, sepString); lib.ListAllStringsNoWeight(fst, 0, sysLister); } } else { String msg = "(language exceeds displayLimit: " + displayLimit + ")"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } return data; } public Object visit(ASTexception_statement node, Object data) { // one required daughter, an ASTregexp; this is the // exception message (it must denote a language of one // string) node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); try { String excMsg = lib.GetSingleString(fst, "The message to an exception statement must denote a language of exactly one string."); throw new KleeneInterpreterException(excMsg); } catch (Exception exc) { exc.printStackTrace(); return data; } } public Object visit(ASTprintln_statement node, Object data) { // one daughter, an ASTregexp; this is the net to list node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); if (!lib.IsAcceptor(fst)) { throw new KleeneArgException("First arg to print must denote a language, not a relation."); } String sepString = "\n"; int displayLimit = 100; long stringCount = lib.NumPaths(fst); PseudoTerminalInternalFrame terminal = null; if (((InterpData) data).getInGUI()) { terminal = ((InterpData) data).getGUI().getTerminal(); } if (stringCount == 0) { String msg = "(language is empty)"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } else if (stringCount == -1) { // then has loops, infinite language String msg = "(language is infinite)"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } else if (stringCount <= displayLimit) { // then just list them (parameterize this figure later) if (terminal != null) { FstStringLister lister = new FstStringLister(terminal, symmap); // native function; second arg 0 is for input side, 1 for output side lib.ListAllStrings(fst, 0, lister); } else { FstSystemStringLister sysLister = new FstSystemStringLister(symmap, sepString); lib.ListAllStrings(fst, 0, sysLister); } } else { String msg = "(language exceeds displayLimit: " + displayLimit + ")"; if (terminal != null) { terminal.appendToHistory(msg); } else { System.out.println(msg); } } return data; } public Object visit(ASTsys_print_statement node, Object data) { // the first required daughter, an ASTregexp; this is the // net to list // second optional daughter is the separator to print after // each string node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); if (!lib.IsAcceptor(fst)) { throw new KleeneArgException("First arg to print must denote a language, not a relation."); } String sepString = "\n"; if (node.jjtGetNumChildren() == 2) { node.jjtGetChild(1).jjtAccept(this, data); Fst sepFst = (Fst) (stack.pop()); sepString = lib.GetSingleString(sepFst, "Second arg to print must denote a language of exactly one string."); } int displayLimit = 100; long stringCount = lib.NumPaths(fst); if (stringCount == 0) { String msg = "(language is empty)"; System.out.println(msg); } else if (stringCount == -1) { // then has loops, infinite language String msg = "(language is infinite)"; System.out.println(msg); } else if (stringCount <= displayLimit) { // then just list them (parameterize this figure later) FstSystemStringLister sysLister = new FstSystemStringLister(symmap, sepString); lib.ListAllStrings(fst, 0, sysLister); } else { String msg = "(language exceeds displayLimit: " + displayLimit + ")"; System.out.println(msg); } return data; } public Object visit(ASTsys_println_statement node, Object data) { // one daughter, an ASTregexp; this is the net to list node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); if (!lib.IsAcceptor(fst)) { throw new KleeneArgException("First arg to print must denote a language, not a relation."); } String sepString = "\n"; int displayLimit = 100; long stringCount = lib.NumPaths(fst); if (stringCount == 0) { String msg = "(language is empty)"; System.out.println(msg); } else if (stringCount == -1) { // then has loops, infinite language String msg = "(language is infinite)"; System.out.println(msg); } else if (stringCount <= displayLimit) { // then just list them (parameterize this figure later) FstSystemStringLister sysLister = new FstSystemStringLister(symmap, sepString); lib.ListAllStrings(fst, 0, sysLister); } else { String msg = "(language exceeds displayLimit: " + displayLimit + ")"; System.out.println(msg); } return data; } public Object visit(ASTtestTokensTextFile_statement node, Object data) { // Total: 11 regexp arguments, syntactically constrained // // 0. the Fst to test node.jjtGetChild(0).jjtAccept(this, data); Fst testFst = (Fst) (stack.pop()); // 1. path of the input file node.jjtGetChild(1).jjtAccept(this, data); Fst tempFst = (Fst) (stack.pop()); String inputFilePath = lib.GetSingleString(tempFst, "Second arg to testTokensTextFile must denote a language of exactly one string."); if (inputFilePath.length() == 0) { throw new KleeneArgException( "Second arg to testTokensTextFile must denote a language of exactly one non-empty string"); } // 2. encoding of the input file node.jjtGetChild(2).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String inputFileEncoding = lib.GetSingleString(tempFst, "Third arg to testTokensTextFile must denote a language of exactly one string."); if (inputFileEncoding.length() == 0) { throw new KleeneArgException("Third arg to testTokensTextFile must denote one non-empty string"); } // 3. path of the output file node.jjtGetChild(3).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String outputFilePath = lib.GetSingleString(tempFst, "Fourth arg to testTokensTextFile must denote a language of exactly one string."); if (outputFilePath.length() == 0) { throw new KleeneArgException("Fourth arg to testTokensTextFile must denote one non-empty string"); } // 4. encoding of the output file node.jjtGetChild(4).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String outputFileEncoding = lib.GetSingleString(tempFst, "Fifth arg to testTokensTextFile must denote a language of exactly one string."); if (outputFileEncoding.length() == 0) { throw new KleeneArgException("Fifth arg to testTokensTextFile must denote one non-empty string"); } // And for the XML output // 5. name of the root element node.jjtGetChild(5).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String rootElmtName = lib.GetSingleString(tempFst, "Sixth arg to testTokensTextFile must denote a language of exactly one string."); if (rootElmtName.length() == 0) { throw new KleeneArgException("Sixth arg to testTokensTextFile must denote one non-empty string"); } // 6. name of the token element node.jjtGetChild(6).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String tokenElmtName = lib.GetSingleString(tempFst, "Seventh arg to testTokensTextFile must denote a language of exactly one string."); if (tokenElmtName.length() == 0) { throw new KleeneArgException("Seventh arg to testTokensTextFile must denote one non-empty string"); } // 7. name of the input element node.jjtGetChild(7).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String inputElmtName = lib.GetSingleString(tempFst, "Eighth arg to testTokensTextFile must denote a language of exactly one string."); if (inputElmtName.length() == 0) { throw new KleeneArgException("Eighth arg to testTokensTextFile must denote one non-empty string"); } // 8. name of the outputs element (N.B. plural) node.jjtGetChild(8).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String outputsElmtName = lib.GetSingleString(tempFst, "Ninth arg to testTokensTextFile must denote a language of exactly one string."); if (outputsElmtName.length() == 0) { throw new KleeneArgException("Ninth arg to testTokensTextFile must denote one non-empty string"); } // 9. name of the output element (N.B. singular) node.jjtGetChild(9).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String outputElmtName = lib.GetSingleString(tempFst, "Tenth arg to testTokensTextFile must denote a language of exactly one string."); if (outputElmtName.length() == 0) { throw new KleeneArgException("Tenth arg to testTokensTextFile must denote one non-empty string"); } // 10. name of the weight attr in the output elmt node.jjtGetChild(10).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String weightAttrName = lib.GetSingleString(tempFst, "Eleventh arg to testTokensTextFile must denote a language of exactly one string."); if (weightAttrName.length() == 0) { throw new KleeneArgException("Eleventh arg to testTokensTextFile must denote one non-empty string"); } String fullpath = getFullpath(inputFilePath); TranslitTokenizerBuilder ttb = new TranslitTokenizerBuilder(symmap, testFst.getSigma(), lib); lib.Iterate4mcs(testFst, ttb, symmap.getStartPuaCpv()); Transliterator trInput = ttb.getTranslitTokenizer(true); // true for input side try { BufferedReader in = null; if (inputFileEncoding.equals("default") || inputFileEncoding.equals("-")) { // get the current default encoding of the operating system inputFileEncoding = System.getProperty("file.encoding"); } if (inputFileEncoding.equals("UTF-8")) { in = new BufferedReader(new InputStreamReader( new UTF8BOMStripperInputStream(new FileInputStream(fullpath)), inputFileEncoding)); } else { in = new BufferedReader(new InputStreamReader(new FileInputStream(fullpath), inputFileEncoding)); } // now try to open the output file fullpath = getFullpath(outputFilePath); BufferedWriter out = null; if (outputFileEncoding.equals("default") || outputFileEncoding.equals("-")) { // get the current default encoding of the operating system outputFileEncoding = System.getProperty("file.encoding"); } out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fullpath), outputFileEncoding)); out.write("<?xml version=\"1.0\" encoding=\"" + outputFileEncoding + "\"?>"); out.newLine(); out.write("<" + rootElmtName + ">"); out.newLine(); // read the input string/words, one per line, from the input file, write output to the output file XMLOutputLister xmlOutputLister = new XMLOutputLister(symmap, out, outputElmtName, weightAttrName); String token; // one per line in the input file Fst modifiedTestFst; while ((token = in.readLine()) != null) { String cpvstr = trInput.transliterate(token); // converts cpvstr to a sequence of code pt values, and // each one could fill one or two 16-bit code units; // this is where multichar symbols are reduced to their // code point values // get length in Unicode characters (not code units) int inputlen = cpvstr.codePointCount(0, cpvstr.length()); // allocate an int array to hold those code-point values, // one int per code point value int[] cpvArray = new int[inputlen]; // UCharacterIterator knows how to iterate over a String and // return the Unicode-Character code point values UCharacterIterator iter = UCharacterIterator.getInstance(cpvstr); // we need to build each input string into a one-path Fst // store the codepoints in the int array (which will be passed to // oneStringNativeFst(), a native method int codepoint; int index = 0; while ((codepoint = iter.nextCodePoint()) != UCharacterIterator.DONE) { // any multichar symbols will already be in the // symmap, or they wouldn't have been identified; // but BMP characters may not yet be in the symmap if (Character.charCount(codepoint) == 1) { symmap.putsym(String.valueOf((char) codepoint)); } cpvArray[index++] = codepoint; } // 0 arg means generate Fst compFst = lib.ApplyToOneString(testFst, cpvArray, 0); // prepare to list the output strings (and their weights) long stringCount = lib.NumPaths(compFst); // XML output for this input token out.write(" <" + tokenElmtName + ">"); out.newLine(); // be careful to escape XML special chars in line; // N.B. escapeXml also escapes non-ASCII Unicode letters //out.write(" <" + inputElmtName + ">" + // StringEscapeUtils.escapeXml(token) + "</" + // inputElmtName + ">") ; out.write(" <" + inputElmtName + ">" + EscapeXML.escapeXML(token) + "</" + inputElmtName + ">"); out.newLine(); out.write(" <" + outputsElmtName + ">"); out.newLine(); if (stringCount == 0) { // output nothing } else if (stringCount == -1) { // means that the composedFstPtr has loops, // denotes an infinite language out.write(" <infinite/>"); out.newLine(); } else { // native function listAllStrings will find all // strings in the Fst // and make callbacks to xmlOutputLister, // which knows how to output them as XML elements lib.ListAllStrings(compFst, 1, xmlOutputLister); } out.write(" </" + outputsElmtName + ">"); out.newLine(); out.write(" </" + tokenElmtName + ">"); out.newLine(); } in.close(); out.write("</" + rootElmtName + ">"); out.newLine(); out.flush(); out.close(); } catch (Exception e) { System.out.println("Exception found while testing input from file."); e.printStackTrace(); } return data; } // testTokensXMLFile_statement // Reads XML output from testTokensTextFile, // which has an <input></input> // element for each token. Extract out just these input strings, // run them again, and produce another XML output file. This allows // comparison of the previous XML output with new XML output, for // regression-testing. public Object visit(ASTtestTokensXMLFile_statement node, Object data) { // Total: 11 regexp arguments, syntactically constrained // // 0. the Fst to test node.jjtGetChild(0).jjtAccept(this, data); Fst testFst = (Fst) (stack.pop()); // 1. path of the input file node.jjtGetChild(1).jjtAccept(this, data); Fst tempFst = (Fst) (stack.pop()); String inputFilePath = lib.GetSingleString(tempFst, "Second arg to testTokensXMLFile must denote a language of exactly one string."); if (inputFilePath.length() == 0) { throw new KleeneArgException( "Second arg to testTokensXMLFile must denote exactly one non-empty string"); } // 2. argument supplying the name of the element holding // the input strings, by default, "input", i.e. // <input>...</input> // N.B. in testTokensTextFile, this argument specifies the // encoding of the input file, which is not needed for XML, // which either has an explicit "encoding" specification, or // is UTF-8 by default node.jjtGetChild(2).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String srcInputElmtName = lib.GetSingleString(tempFst, "Third arg to testTokensXMLFile must denote a language of exactly one string."); if (srcInputElmtName.length() == 0) { throw new KleeneArgException("Third arg to testTokensXMLFile must denote one non-empty string"); } // 3. path of the output file node.jjtGetChild(3).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String outputFilePath = lib.GetSingleString(tempFst, "Fourth arg to testTokensXMLFile must denote a language of exactly one string."); if (outputFilePath.length() == 0) { throw new KleeneArgException("Fourth arg to testTokensXMLFile must denote one non-empty string"); } // 4. encoding of the output file node.jjtGetChild(4).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String outputFileEncoding = lib.GetSingleString(tempFst, "Fifth arg to testTokensXMLFile must denote a language of exactly one string."); if (outputFileEncoding.length() == 0) { throw new KleeneArgException("Fifth arg to testTokensXMLFile must denote one non-empty string"); } // And for the XML output // 5. name of the root element node.jjtGetChild(5).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String rootElmtName = lib.GetSingleString(tempFst, "Sixth arg to testTokensXMLFile must denote a language of exactly one string."); if (rootElmtName.length() == 0) { throw new KleeneArgException("Sixth arg to testTokensXMLFile must denote one non-empty string"); } // 6. name of the token element node.jjtGetChild(6).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String tokenElmtName = lib.GetSingleString(tempFst, "Seventh arg to testTokensXMLFile must denote a language of exactly one string."); if (tokenElmtName.length() == 0) { throw new KleeneArgException("Seventh arg to testTokensXMLFile must denote one non-empty string"); } // 7. name of the input element node.jjtGetChild(7).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String inputElmtName = lib.GetSingleString(tempFst, "Eighth arg to testTokensXMLFile must denote a language of exactly one string."); if (inputElmtName.length() == 0) { throw new KleeneArgException("Eighth arg to testTokensXMLFile must denote one non-empty string"); } // 8. name of the outputs element (N.B. plural) node.jjtGetChild(8).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String outputsElmtName = lib.GetSingleString(tempFst, "Ninth arg to testTokensXMLFile must denote a language of exactly one string."); if (outputsElmtName.length() == 0) { throw new KleeneArgException("Ninth arg to testTokensXMLFile must denote one non-empty string"); } // 9. name of the output element (N.B. singular) node.jjtGetChild(9).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String outputElmtName = lib.GetSingleString(tempFst, "Tenth arg to testTokensXMLFile must denote a language of exactly one string."); if (outputElmtName.length() == 0) { throw new KleeneArgException("Tenth arg to testTokensXMLFile must denote one non-empty string"); } // 10. name of the weight attr in the output elmt node.jjtGetChild(10).jjtAccept(this, data); tempFst = (Fst) (stack.pop()); String weightAttrName = lib.GetSingleString(tempFst, "Eleventh arg to testTokensXMLFile must denote a language of exactly one string."); if (weightAttrName.length() == 0) { throw new KleeneArgException("Eleventh arg to testTokensXMLFile must denote one non-empty string"); } String fullpath = getFullpath(inputFilePath); TranslitTokenizerBuilder ttb = new TranslitTokenizerBuilder(symmap, testFst.getSigma(), lib); lib.Iterate4mcs(testFst, ttb, symmap.getStartPuaCpv()); Transliterator trInput = ttb.getTranslitTokenizer(true); // true for input side try { // try to read/parse the XML input file Document doc = null; doc = parseXML(fullpath); // dom4j // Read all the <input></input> elements into a list // N.B. by default, the name of the element is "input", // but in general it is specified in arg srcInputElmtName List list = doc.selectNodes("//" + srcInputElmtName); // now try to open the output file fullpath = getFullpath(outputFilePath); BufferedWriter out = null; if (outputFileEncoding.equals("default") || outputFileEncoding.equals("-")) { // get the current default encoding of the operating system outputFileEncoding = System.getProperty("file.encoding"); } out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fullpath), outputFileEncoding)); out.write("<?xml version=\"1.0\" encoding=\"" + outputFileEncoding + "\"?>"); out.newLine(); out.write("<" + rootElmtName + ">"); out.newLine(); XMLOutputLister xmlOutputLister = new XMLOutputLister(symmap, out, outputElmtName, weightAttrName); // Loop through the <input></input> elements, extracting and // running the text string from each one; write output to // the output file String token; Fst modifiedTestFst; for (Iterator it = list.iterator(); it.hasNext();) { Element inputElmt = (Element) it.next(); token = inputElmt.getText(); String cpvstr = trInput.transliterate(token); // converts cpvstr to a sequence of code pt values, and // each one could fill one or two 16-bit code units; // this is where multichar symbols are reduced to their // code point values // get length in Unicode characters (not code units) int inputlen = cpvstr.codePointCount(0, cpvstr.length()); // allocate an int array to hold those code-point values, // one int per code point value int[] cpvArray = new int[inputlen]; // UCharacterIterator knows how to iterate over a // String and // return the Unicode-Character code point values UCharacterIterator iter = UCharacterIterator.getInstance(cpvstr); // we need to build each input string into a one-path Fst // store the codepoints in the int array // (which will be passed to // oneStringNativeFst(), a native method int codepoint; int index = 0; while ((codepoint = iter.nextCodePoint()) != UCharacterIterator.DONE) { // any multichar symbols will already be in the // symmap, or they wouldn't have been identified; // but BMP characters may not yet be in the symmap if (Character.charCount(codepoint) == 1) { symmap.putsym(String.valueOf((char) codepoint)); } cpvArray[index++] = codepoint; } // 0 arg for generation, apply the inputFst to the "input" // side of testFst Fst compFst = lib.ApplyToOneString(testFst, cpvArray, 0); // prepare to list the output strings (and their weights) long stringCount = lib.NumPaths(compFst); // XML output for this input token out.write(" <" + tokenElmtName + ">"); out.newLine(); // be careful to escape XML special chars in line; // N.B. escapeXml also escapes non-ASCII Unicode letters //out.write(" <" + inputElmtName + ">" + // StringEscapeUtils.escapeXml(token) + // "</" + inputElmtName + ">") ; out.write(" <" + inputElmtName + ">" + EscapeXML.escapeXML(token) + "</" + inputElmtName + ">"); out.newLine(); out.write(" <" + outputsElmtName + ">"); out.newLine(); if (stringCount == 0) { // output nothing } else if (stringCount == -1) { // means that the compFstPtr has loops, // denotes an infinite language out.write(" <infinite/>"); out.newLine(); } else { // native function listAllStrings will find all // strings in the Fst // and make callbacks to xmlOutputLister, // which knows how to output // them as XML elements lib.ListAllStrings(compFst, 1, xmlOutputLister); } out.write(" </" + outputsElmtName + ">"); out.newLine(); out.write(" </" + tokenElmtName + ">"); out.newLine(); } out.write("</" + rootElmtName + ">"); out.newLine(); out.flush(); out.close(); } catch (Exception e) { // KRB: review this System.out.println("Exception found while testing input from file."); e.printStackTrace(); } return data; } private String getOsName() { String os = ""; if (System.getProperty("os.name").toLowerCase().indexOf("mac") > -1) { os = "osx"; } else if (System.getProperty("os.name").toLowerCase().indexOf("linux") > -1) { os = "linux"; } else if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) { os = "windows"; } else { os = "unknown"; } return os; } private Document parseXMLPrefs(String filepath) throws Exception { SAXReader reader = new SAXReader(); Document document = null; // Document document = reader.read(filepath) ; try { // the encoding should be UTF-8 // then need to work around SUN's irresponsible decision not to // handle the optional UTF-8 BOM correctly document = reader.read( new InputStreamReader(new UTF8BOMStripperInputStream(new FileInputStream(filepath)), "UTF-8")); } catch (Exception e) { e.printStackTrace(); throw e; } return document; } // called in ASTdraw_statement private String getPref(Document doc, String xmlPath) { String osName = getOsName(); // first see if there is a user-specified override of the default value String value = ((Element) doc.selectSingleNode("/prefs/" + osName + "/user/" + xmlPath)).getTextTrim(); // if the user setting is empty if (value.equals("")) { // then get the default setting value = ((Element) doc.selectSingleNode("/prefs/" + osName + "/default/" + xmlPath)).getTextTrim(); } return value; } public Object visit(ASTdraw_statement node, Object data) { // one daughter: regexp // KRB: does this make any sense outside of the GUI??? // KRB: review this whole method node.jjtGetChild(0).jjtAccept(this, data); // Should leave an Fst object on the stack (see Fst.java) Fst fst = (Fst) (stack.pop()); // magic numbers for now; limits on the size of a network // that will be drawn long stateLimit = 400L; long arcLimit = 400L; long nstates = lib.NumStates(fst); long narcs = lib.NumArcs(fst); if (nstates > stateLimit) { // don't try to draw it outputInterpMessage("// Fst contains over " + stateLimit + " states, which is generally too much for the dot application to handle.", data); return data; } if (narcs > arcLimit) { // don't try to draw it outputInterpMessage("// Fst contains over " + arcLimit + " arcs, which is generally too much for the dot application to handle.", data); return data; } String userHomeDir = System.getProperty("user.home"); // to find temp files like ~/.kleene/tmp/last.dot // and ~/.kleene/prefs/prefs.xml String osName = getOsName(); // defaults String tmpdir = ""; String prefsPath = ""; String dotSrcPath = ""; String slashSep = "/"; if (osName.equals("windows")) { slashSep = "\\"; // only really needed for command shell (cmd /c), // else can use "/" } StringBuilder sbhex = new StringBuilder(); StringBuilder sb = new StringBuilder(); getSigmaStrings(fst, sbhex, sb); // On Linux and OS X, the basic Kleene directory is ~/.kleene // On Windows, this maps to C:\Documents and Settings\ // \<username>\.kleene tmpdir = userHomeDir + slashSep + ".kleene" + slashSep + "tmp"; dotSrcPath = tmpdir + slashSep + "last.dot"; prefsPath = userHomeDir + slashSep + ".kleene" + slashSep + "prefs" + slashSep + "prefs.xml"; // an FstDotWriter object knows how to write a GraphViz .dot source file (to a specified // file; here written to last.lot in the user's tmp/ directory) FstDotWriter fstDotWriter = new FstDotWriter(symmap, new File(dotSrcPath), sb.toString(), "UTF-8"); // call Fst2dot traverses an OpenFst Fst directly and generates // dot code (by making callbacks to methods in the Java fstDotWriter) lib.Fst2dot(fst, fstDotWriter); // we should now have tmp/last.dot (a GraphViz dot source file // describing a network diagram) // If the osName is "osx" and a native Graphviz.app is installed // in /Applications, then things are simple. Just call Graphviz directly // on the .dot source file. "open -a Graphviz /path/to/last.lot" // No need to generate PostScript and then call a viewer to see it. File nativeGraphviz = new File("/Applications/Graphviz.app"); if (osName.equals("osx") && nativeGraphviz.exists()) { try { Process proc = Runtime.getRuntime().exec("open -a Graphviz " + dotSrcPath); try { if (proc.waitFor() != 0) { System.err.println("Problem calling native OS X GraphViz: exit value " + proc.exitValue()); } } catch (InterruptedException e) { System.err.println(e); } finally { } } catch (Exception e) { System.err.println(e); } } else { // Need to do it the hard way. // // Take the .dot source file and call 'dot' to generate a graphics file, e.g. .ps // Then take the graphics file and call a viewer application // The location of the 'dot' application, the graphics format, and the view // application are specified in the user-specific pref.xml file // Access the user-specific prefs.xml file // type Document is a Java object representing an XML document (typically // read from an XML file into memory) Document doc = null; try { doc = parseXMLPrefs(prefsPath); // parse the user's prefs/prefs.xml } catch (Exception e) { // KRB: review this System.out.println("Problem reading ~/.kleene/prefs/prefs.xml"); e.printStackTrace(); } // Navigate to platform-specific and user-specific dot, format, // viewer elmts in the prefs.xml file // get the path to the "dot" application String dotpath = getPref(doc, "dot/dotpath"); // get the file format the dot should produce, e.g. ps or pdf String dotflag = getPref(doc, "dot/dotflag"); // get the path to the viewer application String dotview = getPref(doc, "dot/viewer"); // Trouble with generating/displaying PDF directly; // If you generate .ps and 'open' it, the orientation=landscape // and center="true" are reflected correctly in the display (the // ps is converted automatically to pdf) // But if you generate the PDF file directly and 'open' it, the // orientation is wrong and the centering command is ignored. // PostScript seems more reliable right now. // ****************** Call 'dot' from Java ********************** // construct the 'dot' command string to be launched by ProcessBuilder // Command shell prefix needed for ProcessBuilder is opsys-specific. String cmdShell, cmdShellOpts; if (osName.equals("windows")) { cmdShell = "cmd"; cmdShellOpts = "/c"; } else { // for Linux and OS X (valued of osName will be "osx") cmdShell = "/bin/sh"; cmdShellOpts = "-c"; } // Use doublequotes to support filenames with embedded spaces. // Initial blank prevents undesired doublequote removal by // Windows cmd.exe (see "cmd /?"). String cmd = " \"" + dotpath + "\"" + " -T" + dotflag + " \"" + tmpdir + slashSep + "last.dot\"" + " > " + "\"" + tmpdir + slashSep + "last." + dotflag + "\""; // calling 'dot' from Java, from the .dot source file, // it should generate a graphics file, e.g. .ps (PostScript) try { ProcessBuilder pb = new ProcessBuilder(cmdShell, cmdShellOpts, cmd); Process p = pb.start(); StreamFlusher errorFlusher = new StreamFlusher(p.getErrorStream(), "ERROR"); StreamFlusher outputFlusher = new StreamFlusher(p.getInputStream(), "OUTPUT"); errorFlusher.start(); outputFlusher.start(); int exitVal = p.waitFor(); } catch (Exception e) { e.printStackTrace(); } // ******************* Now launch the viewer app from Java //KRB: putting double quotes around dotview currently // works for Linux, at least // with the current default dotview string: /usr/bin/kghostview // which doesn't contain command-line options if (osName.equals("osx")) { // KRB: putting double quotes around dotview breaks // drawing for OS X, // where the dotview string is // /usr/bin/open -a /Applications/Preview.app/Contents/MacOS/Preview // (having three fields and two spaces) cmd = dotview + " \"" + tmpdir + slashSep + "last." + dotflag + "\""; } else { // Phil: fix for Windows (and seems to work for Linux) // Use doublequotes to support filenames with embedded spaces. // Initial blank prevents undesired doublequote removal by Windows cmd.exe (see "cmd /?"). cmd = " \"" + dotview + "\" \"" + tmpdir + slashSep + "last." + dotflag + "\""; } // launching the viewer on the ps, pdf (or whatever) file generated by 'dot' try { ProcessBuilder pb = new ProcessBuilder(cmdShell, cmdShellOpts, cmd); Process p = pb.start(); StreamFlusher errorFlusher = new StreamFlusher(p.getErrorStream(), "ERROR"); StreamFlusher outputFlusher = new StreamFlusher(p.getInputStream(), "OUTPUT"); errorFlusher.start(); outputFlusher.start(); // if active, this stmt causes the viewer window to be 'modal', causing // Kleene to suspend operations until the viewer is closed //int exitVal = p.waitFor() ; } catch (Exception e) { e.printStackTrace(); } // need to drain stdout stderr and inputStream in separate threads? } return data; } public Object visit(ASTsigma_statement node, Object data) { // Makes sense only in the GUI if (((InterpData) data).getInGUI()) { // Should be just one Fst daughter, syntactically constrained node.jjtGetChild(0).jjtAccept(this, data); Fst fst = (Fst) (stack.pop()); PseudoTerminalInternalFrame terminal = ((InterpData) data).getGUI().getTerminal(); //String str = fst.getSigma().toString() ; //terminal.appendToHistory(str) ; StringBuilder sbhex = new StringBuilder(); StringBuilder sb = new StringBuilder(); getSigmaStrings(fst, sbhex, sb); terminal.appendToHistory("{ " + sbhex.toString() + "}"); terminal.appendToHistory("{ " + sb.toString() + "}"); if (fst.getContainsOther()) { terminal.appendToHistory("Contains OTHER"); } } return data; } public Object visit(ASTwritexml_statement node, Object data) { // Either one, two or three daughters: // fst (, filepath (, encoding)?)? // The first represents the Fst to be drawn as XML node.jjtGetChild(0).jjtAccept(this, data); // Should leave an Fst object on the stack Fst fst = (Fst) (stack.pop()); String path = "out.xml"; // default if (node.jjtGetNumChildren() >= 2) { node.jjtGetChild(1).jjtAccept(this, data); Fst pathFst = (Fst) (stack.pop()); path = lib.GetSingleString(pathFst, "Second arg to writeXml must denote a language of exactly one string."); if (path.length() == 0) { throw new KleeneArgException("Second arg to writeXml must denote a non-empty string"); } } String encoding = "UTF-8"; // default if (node.jjtGetNumChildren() == 3) { node.jjtGetChild(2).jjtAccept(this, data); Fst encodingFst = (Fst) (stack.pop()); encoding = lib.GetSingleString(encodingFst, "Second arg to writeXml must denote a language of exactly one string."); if (path.length() == 0) { throw new KleeneArgException("Second arg to writeXml must denote a non-empty string"); } } String fullpath = getFullpath(path); // note that the FstXmlWriter gets the filepath, so it knows where // to write the file writeXmlHelper(fst, fullpath, encoding); return data; } public Object visit(ASTwritexml_state_oriented_statement node, Object data) { // One, two, three or four daughters: the first is required // fst (, filepath (, name (, encoding )?)?)? // The first is a regexp that represents the Fst to be written as XML node.jjtGetChild(0).jjtAccept(this, data); // Should leave an Fst object on the stack Fst fst = (Fst) (stack.pop()); String path = "out.xml"; // default name of output file if (node.jjtGetNumChildren() >= 2) { node.jjtGetChild(1).jjtAccept(this, data); Fst pathFst = (Fst) (stack.pop()); path = lib.GetSingleString(pathFst, "Second arg to writeXml must denote a language of exactly one string."); if (path.length() == 0) { throw new KleeneArgException("Second arg to writeXml must denote a non-empty string"); } } String name = "Out"; // default name of code file, e.g. Out.java if (node.jjtGetNumChildren() >= 3) { node.jjtGetChild(2).jjtAccept(this, data); Fst nameFst = (Fst) (stack.pop()); name = lib.GetSingleString(nameFst, "Third arg to writeXml must denote a language of exactly one string."); if (name.length() == 0) { throw new KleeneArgException("Third arg to writeXml must denote a non-empty string"); } } String encoding = "UTF-8"; // default if (node.jjtGetNumChildren() == 4) { node.jjtGetChild(3).jjtAccept(this, data); Fst encodingFst = (Fst) (stack.pop()); encoding = lib.GetSingleString(encodingFst, "Fourth arg to writeXml must denote a language of exactly one string."); if (encoding.length() == 0) { throw new KleeneArgException("Fourth arg to writeXml must denote a non-empty string"); } } String fullpath = getFullpath(path); // note that the FstXmlWriter gets the filepath, so it knows where // to write the file writeXmlHelperStateOriented(fst, fullpath, name, encoding); return data; } public Object visit(ASTwritedot_statement node, Object data) { // either one, two or three daughters // regexp (, filepath (, encoding)?)? // the first represents the Fst to be drawn as DOT source node.jjtGetChild(0).jjtAccept(this, data); // Should leave an Fst object on the stack Fst fst = (Fst) (stack.pop()); String path = "out.dot"; // default if (node.jjtGetNumChildren() >= 2) { node.jjtGetChild(1).jjtAccept(this, data); Fst pathFst = (Fst) (stack.pop()); path = lib.GetSingleString(pathFst, "Second arg to writeDot must denote a language of exactly one string."); if (path.length() == 0) { throw new KleeneArgException("Second arg to writeDot must denote a non-empty string"); } } String encoding = "UTF-8"; // default if (node.jjtGetNumChildren() == 3) { node.jjtGetChild(2).jjtAccept(this, data); Fst encodingFst = (Fst) (stack.pop()); encoding = lib.GetSingleString(encodingFst, "Third arg to writeDot must denote a language of exactly one string."); if (encoding.length() == 0) { throw new KleeneArgException("Third arg to writeDot must denote a non-empty string"); } } StringBuilder sbhex = new StringBuilder(); StringBuilder sb = new StringBuilder(); getSigmaStrings(fst, sbhex, sb); String fullpath = getFullpath(path); // note that the FstDotWriter gets the filepath, so it knows where // to write the file FstDotWriter fstDotWriter = new FstDotWriter(symmap, new File(fullpath), sb.toString(), encoding); // Call Fst2dot to iterate through the Fst, // it will make calls // back to methods in the fstDotWriter to do the actual output // to file. // (The C++ code has iterators, but Unicode file output // from C++ is not // worth the trouble. Even if the C++ code were written to // write the DOT // directly, it would still have to make calls back to the // symmap method // .getsym(i) to convert the int-value labels to strings. lib.Fst2dot(fst, fstDotWriter); return data; } public Object visit(ASTsource_statement node, Object data) { // Syntax: // source regexp() ; // one filepath, to be read in the default encoding of the // operating system; // source regexp(), regexp() ; // pair of (filepath, encoding) // source regexp(), regexp(), regexp(), regexp(), ... ; // pairs of (filepath, // encoding) // // each regexp() semantically limited // to encoding a single string int childCount = node.jjtGetNumChildren(); String pathstring = ""; String fullpath = ""; String encoding = ""; for (int p = 0; (childCount == 1 && p == 0) || p <= (childCount - 2); p += 2) { // get the path string node.jjtGetChild(p).jjtAccept(this, data); // Should leave an Fst object on the stack Fst pathFst = (Fst) (stack.pop()); pathstring = lib.GetSingleString(pathFst, "Each arg to 'source' must denote a language of exactly one string."); if (pathstring.length() == 0) { throw new KleeneArgException( "Each path arg to 'source' must denote a language of exactly one non-empty string"); } File file = new File(pathstring); fullpath = getFullpath(pathstring); // get the encoding ("default" iff childCount == 1) if (childCount == 1) { // get the current default encoding of the operating system encoding = System.getProperty("file.encoding"); } else { node.jjtGetChild(p + 1).jjtAccept(this, data); Fst encodingFst = (Fst) (stack.pop()); encoding = lib.GetSingleString(encodingFst, "Each arg to 'source' must denote a language of exactly one string."); if (encoding.length() == 0) { throw new KleeneArgException("The encoding argument to source must be a non-empty string."); } } // Now have fullpath and encoding if (((InterpData) data).getInGUI()) { // path, encoding, inGUI Kleene.runScript(fullpath, encoding, true); } else { // not in a GUI, so handle like a command-line script Kleene.runScript(fullpath, encoding, false); } } return data; } }