// This file is part of AceWiki. // Copyright 2008-2013, AceWiki developers. // // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU // Lesser General Public License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If // not, see http://www.gnu.org/licenses/. package ch.uzh.ifi.attempto.chartparser; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This class represents the parse tree of a successfully parsed text. * * @author Tobias Kuhn */ public class ParseTree { private ParseTreeNode topNode; private String lamFunctor = "lam"; private String appFunctor = "app"; private String concatFunctor = "+"; private String semLabel = "sem"; /** * Creates a new parse tree object. * * @param topNode The top node. */ ParseTree(Edge edge) { this.topNode = new ParseTreeNode(edge.deepCopy(true)); } private ParseTree(ParseTreeNode topNode) { this.topNode = topNode; } private ParseTree createParseTree(ParseTreeNode topNode) { ParseTree newParseTree = new ParseTree(topNode); newParseTree.lamFunctor = lamFunctor; newParseTree.appFunctor = appFunctor; newParseTree.concatFunctor = concatFunctor; newParseTree.semLabel = semLabel; return newParseTree; } /** * Returns the start position of this tree. This can be relevant for subtrees. * * @return The start position. */ public int getStartPos() { return topNode.getStartPos(); } /** * Returns the end position of this tree. This can be relevant for subtrees. * * @return The end position. */ public int getEndPos() { return topNode.getEndPos(); } /** * Sets the name of the annotation item that contains the semantics information. The default is * "sem". * * @param semLabel The name of the annotation item containing the semantics. */ public void setSemanticsLabel(String semLabel) { this.semLabel = semLabel; } /** * Sets the functor of the lambda function for the calculation of lambda semantics. The default * is "lam". * * @param lamFunctor The lambda functor. */ public void setLambdaFunctor(String lamFunctor) { this.lamFunctor = lamFunctor; } /** * Sets the functor of the application function for the calculation of lambda semantics. The * default is "app". * * @param appFunctor The application functor. */ public void setApplicationFunctor(String appFunctor) { this.appFunctor = appFunctor; } /** * Sets the functor of the concatenation function for the calculation of semantics. The default * is "+". Terms using the concatenation functor are flattened. The functor can be set to null * to avoid flattening. * * @param concatFunctor The concatentation functor. */ public void setConcatFunctor(String concatFunctor) { this.concatFunctor = concatFunctor; } /** * Returns the top node of the parse tree. * * @return The top node of the parse tree. */ public ParseTreeNode getTopNode() { return topNode; } /** * Returns the syntax tree. The leaves of the tree are objects of Category. All other nodes are * arrays of Object containing the child nodes. * * @return The syntax tree. */ public Object getSynTree() { return getSynTree(topNode); } private Object getSynTree(ParseTreeNode n) { Category h = n.getCategory(); List<ParseTreeNode> c = n.getChildren(); Object[] o = new Object[c.size()+1]; o[0] = h; for (int i = 0 ; i < c.size() ; i++) { o[i+1] = getSynTree(c.get(i)); } return o; } /** * Returns a serialization of the syntax tree. * * @return A serialization of the syntax tree. */ public String getSerializedSynTree() { return serializeStructure(getSynTree()); } /** * Returns an ASCII representation of the syntax tree. * * @return An ASCII representation of the syntax tree. */ public String getAsciiSynTree() { return structureToAsciiTree(getSynTree(), 0); } /** * Returns the semantics tree. The semantics are retrieved from the annotations of the grammar * rules. The leaves of the tree are objects of String or StringRef. All other nodes are arrays * of Object containing the child nodes. * * @return The semantics tree. */ public Object getSemTree() { Object o = getSemTree(topNode); if (concatFunctor != null) { o = applyConcatenation(o); } return o; } private Object getSemTree(ParseTreeNode n) { Object structure = n.getAnnotation().getItem(semLabel); if (structure == null) return null; for (ParseTreeNode c : n.getChildren()) { Object o = getSemTree(c); if (o != null) { structure = new Object[] {appFunctor, structure, o}; } } return structure; } /** * Returns a serialization of the semantics tree. * * @return A serialization of the semantics tree. */ public String getSerializedSemTree() { return serializeStructure(getSemTree()); } /** * Returns an ASCII representation of the semantics tree. * * @return An ASCII representation of the semantics tree. */ public String getAsciiSemTree() { return structureToAsciiTree(getSemTree(), 0); } /** * Returns the semantics tree, interpreted as a lambda expression. The returned tree is beta- * reduced. The semantics are retrieved from the annotations of the grammar rules. The leaves * of the tree are objects of String or StringRef. All other nodes are arrays of Object * containing the child nodes. * * @return The beta-reduced semantics tree. */ public Object getLambdaSemTree() { Object o = getSemTree(topNode); Map<Integer, Object> replace = new HashMap<Integer, Object>(); replace.put(-1, ""); while (replace.containsKey(-1)) { replace.remove(-1); o = applyBetaReduction(o, replace); } if (concatFunctor != null) { o = applyConcatenation(o); } return o; } /** * Returns a serialization of the semantics tree under lambda interpretation. * * @return A serialization of the lambda semantics tree. */ public String getSerializedLambdaSemTree() { return serializeStructure(getLambdaSemTree()); } /** * Returns an ASCII representation of the semantics tree under lambda interpretation. * * @return An ASCII representation of the lambda semantics tree. */ public String getAsciiLambdaSemTree() { return structureToAsciiTree(getLambdaSemTree(), 0); } /** * Returns all subtrees that have the given category name as their top node. In the case of * nested nodes with the given category, only the top-most subtree (containing all other * potential subtrees) is returned. * * @param categoryName The category name. * @return A list of all matching subtrees. */ public List<ParseTree> getSubTrees(String categoryName) { List<ParseTreeNode> topNodeList = new ArrayList<ParseTreeNode>(); topNodeList.add(topNode); List<ParseTree> subTrees = new ArrayList<ParseTree>(); collectSubTrees(categoryName, topNodeList, subTrees); return subTrees; } private void collectSubTrees(String categoryName, List<ParseTreeNode> nodes, List<ParseTree> subTrees) { for (ParseTreeNode n : nodes) { if (n.getCategory().getName().equals(categoryName)) { subTrees.add(createParseTree(n)); } else { collectSubTrees(categoryName, n.getChildren(), subTrees); } } } /** * Returns the list of terminals of this tree. * * @return The list of terminals. */ public List<Terminal> getTerminals() { return topNode.getTerminals(); } private Object applyBetaReduction(Object obj, Map<Integer, Object> replace) { // TODO improve this (just one run, see Blackburn & Bos) if (obj == null) { return null; } else if (obj instanceof String) { return obj; } else if (obj instanceof StringRef) { StringRef sr = (StringRef) obj; if (replace.containsKey(sr.getID())) { replace.put(-1, ""); return applyBetaReduction(replace.get(sr.getID()), replace); } else { return obj; } } else if (obj instanceof Object[]) { Object[] a = (Object[]) obj; if (a.length == 0) { return obj; } Object[] c = new Object[a.length]; for (int i = 0 ; i < a.length ; i++) { c[i] = applyBetaReduction(a[i], replace); } if (a.length == 3 && appFunctor.equals(a[0]) && a[1] instanceof Object[]) { Object[] l = (Object[]) a[1]; if (l.length == 3 && lamFunctor.equals(l[0]) && l[1] instanceof StringRef) { replace.put(((StringRef) l[1]).getID(), a[2]); replace.put(-1, ""); return applyBetaReduction(l[2], replace); } } return c; } return obj; } private String serializeStructure(Object obj) { if (obj instanceof Object[]) { Object[] a = (Object[]) obj; if (a.length == 0) { return "*empty*"; } else if (a.length == 1) { return elementToString(a[0]); } else { String s = elementToString(a[0]) + "("; for (int i = 1 ; i < a.length ; i++) { s += serializeStructure(a[i]) + ", "; } if (s.endsWith(", ")) s = s.substring(0, s.length()-2); return s + ")"; } } else { return elementToString(obj); } } private String structureToAsciiTree(Object obj, int tab) { String t = ""; for (int i=0 ; i < tab ; i++) t += " "; if (obj instanceof Object[]) { Object[] a = (Object[]) obj; if (a.length == 0) { t += "*empty*\n"; } else { t += elementToString(a[0]) + "\n"; for (int i = 1 ; i < a.length ; i++) { t += structureToAsciiTree(a[i], tab+1); } } } else { return t + elementToString(obj) + "\n"; } return t; } private String elementToString(Object obj) { if (obj == null) { return "*null*"; } else if (obj instanceof String) { String s = (String) obj; if (s.matches("[a-zA-Z0-9_]+")) { return s; } else { return "'" + s + "'"; } } else if (obj instanceof StringRef) { StringRef sr = (StringRef) obj; if (sr.getString() == null) { return "?" + sr.getID(); } else { return elementToString(sr.getString()); } } else if (obj instanceof Terminal) { return obj.toString(); } else if (obj instanceof Preterminal) { return "$" + ((Preterminal) obj).getName(); } else if (obj instanceof Category) { return ((Category) obj).getName(); } return "*invalid*"; } private Object applyConcatenation(Object obj) { if (isConcatFunction(obj)) { Object[] a = (Object[]) obj; List<Object> cl = new ArrayList<Object>(); for (int i = 1 ; i < a.length ; i++) { Object ci = applyConcatenation(a[i]); if (isConcatFunction(ci)) { Object[] ai = (Object[]) ci; for (int j = 1 ; j < ai.length ; j++) { addFlat(cl, ai[j]); } } else if (!"".equals(ci)) { addFlat(cl, ci); } } if (cl.size() == 0) { return ""; } else if (cl.size() == 1) { return cl.get(0); } else { cl.add(0, concatFunctor); return cl.toArray(); } } else if (obj instanceof Object[]) { Object[] a = (Object[]) obj; Object[] c = new Object[a.length]; for (int i = 0 ; i < a.length ; i++) { c[i] = applyConcatenation(a[i]); } return c; } else { return obj; } } private boolean isConcatFunction(Object obj) { if (!(obj instanceof Object[])) return false; Object[] a = (Object[]) obj; if (a.length == 0) return false; if (!(a[0] instanceof String)) return false; if (!a[0].toString().equals(concatFunctor)) return false; return true; } private void addFlat(List<Object> list, Object obj) { if (obj instanceof StringRef && ((StringRef) obj).getString() != null) { obj = ((StringRef) obj).getString(); } if (list.isEmpty() || !(obj instanceof String)) { list.add(obj); } else { if (list.get(list.size()-1) instanceof String) { String s = (String) list.remove(list.size()-1); list.add(s + obj); } else { list.add(obj); } } } }