/* * * Copyright 2012 lexergen. * This file is part of lexergen. * * lexergen is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * lexergen is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with lexergen. If not, see <http://www.gnu.org/licenses/>. * * lexergen: * A tool to chunk source code into tokens for further processing in a compiler chain. * * Projectgroup: bi, bii * * Authors: Johannes Dahlke * * Module: Softwareprojekt Übersetzerbau 2012 * * Created: Apr. 2012 * Version: 1.0 * */ package de.fuberlin.bii.regextodfaconverter.directconverter.regex.operatortree; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.concurrent.CopyOnWriteArrayList; import de.fuberlin.bii.regextodfaconverter.directconverter.lrparser.grammar.Symbol; import de.fuberlin.bii.regextodfaconverter.directconverter.regex.RegexCharSet; import de.fuberlin.bii.regextodfaconverter.directconverter.syntaxtree.SyntaxTreeException; import de.fuberlin.bii.regextodfaconverter.directconverter.syntaxtree.node.TreeNode; import de.fuberlin.bii.regextodfaconverter.directconverter.syntaxtree.node.TreeNode; import de.fuberlin.bii.regextodfaconverter.directconverter.syntaxtree.node.TreeNodeCollection; import de.fuberlin.bii.regextodfaconverter.directconverter.syntaxtree.node.TreeNodeSet; import de.fuberlin.bii.regextodfaconverter.directconverter.syntaxtree.node.Leaf; import de.fuberlin.bii.utils.Sets; import de.fuberlin.bii.utils.Test; /** * Attributiert einen Operatorbaum. * * @author Johannes Dahlke * * @see AttributizedOperatorTree * @see RegexOperatorTree */ @SuppressWarnings("rawtypes") public class OperatorTreeAttributor<StatePayloadType extends Serializable> { private HashMap<TreeNode,TreeNodeCollection> followPositions = new HashMap<TreeNode, TreeNodeCollection>(); private HashMap<TreeNode,TreeNodeCollection> lastPositions = new HashMap<TreeNode, TreeNodeCollection>(); private HashMap<TreeNode,TreeNodeCollection> firstPositions = new HashMap<TreeNode, TreeNodeCollection>(); private HashMap<TreeNode,Boolean> nullables = new HashMap<TreeNode, Boolean>(); private boolean calculateNullableForNode( TreeNode node) { if ( node instanceof TerminalNode) { // \epsilon-Knoten sind per definition true @SuppressWarnings("unchecked") RegularExpressionElement<StatePayloadType> regexElement = (RegularExpressionElement<StatePayloadType>)((TerminalNode)node).getValue(); if ( regexElement.getValue() == RegexCharSet.EMPTY_STRING) return true; else // Terminale != \epsilon sind nicht nullable return false; } else { // der Knoten enthält eine Operation OperatorNode operatorNode = (OperatorNode) node; switch ( operatorNode.getOperatorType()) { case ALTERNATIVE: return nullable( operatorNode.getLeftChildNode()) || nullable( operatorNode.getRightChildNode()); case CONCATENATION: return nullable( operatorNode.getLeftChildNode()) && nullable( operatorNode.getRightChildNode()); default: // REPETITION return true; } } } private TreeNodeCollection calculateFirstposForNode( TreeNode node) { // \epsilon-Knoten liefern per definition die leere Menge if ( node instanceof TerminalNode) { @SuppressWarnings("unchecked") RegularExpressionElement<StatePayloadType> regexElement = (RegularExpressionElement<StatePayloadType>)((TerminalNode)node).getValue(); if ( regexElement.getValue() == RegexCharSet.EMPTY_STRING) return new TreeNodeSet(); else { // Terminale != \epsilon liefern das aktuelle Element TreeNodeCollection result = new TreeNodeSet(); result.add( node); return result; } } else { // der Knoten enthält eine Operation OperatorNode operatorNode = (OperatorNode) node; switch ( operatorNode.getOperatorType()) { case ALTERNATIVE: {// Vereinigung der firstpos-Mengen TreeNodeCollection result = firstpos( operatorNode.getLeftChildNode()); result = Sets.unionCollections( result, firstpos( operatorNode.getRightChildNode())); return result; } case CONCATENATION: if ( nullable( operatorNode.getLeftChildNode())) { TreeNodeCollection result = firstpos( operatorNode.getLeftChildNode()); result = Sets.unionCollections( result, firstpos( operatorNode.getRightChildNode())); return result; } else { return firstpos( operatorNode.getLeftChildNode()); } default: // REPETITION return firstpos( operatorNode.getLeftChildNode()); } } } private TreeNodeCollection calculateLastposForNode( TreeNode node) { // \epsilon-Knoten liefern per definition die leere Menge if ( node instanceof TerminalNode) { @SuppressWarnings("unchecked") RegularExpressionElement<StatePayloadType> regexElement = (RegularExpressionElement<StatePayloadType>)((TerminalNode)node).getValue(); if ( regexElement.getValue() == RegexCharSet.EMPTY_STRING) return new TreeNodeSet(); else { // Terminale != \epsilon liefern das aktuelle Element TreeNodeCollection result = new TreeNodeSet(); result.add( node); return result; } } else { // der Knoten enthält eine Operation OperatorNode operatorNode = (OperatorNode) node; switch ( operatorNode.getOperatorType()) { case ALTERNATIVE: {// Vereinigung der lastpos-Mengen TreeNodeCollection result = lastpos( operatorNode.getLeftChildNode()); result = Sets.unionCollections( result, lastpos( operatorNode.getRightChildNode())); return result; } case CONCATENATION: if ( nullable( operatorNode.getRightChildNode())) { TreeNodeCollection result = lastpos( operatorNode.getLeftChildNode()); result = Sets.unionCollections( result, lastpos( operatorNode.getRightChildNode())); return result; } else { return lastpos( operatorNode.getRightChildNode()); } default: // REPETITION return lastpos( operatorNode.getLeftChildNode()); } } } private void calculateFollowposForNode( TreeNode node) { // init to prevent null items in followPositions if ( Test.isUnassigned( followPositions.get( node))) { followPositions.put( node, new TreeNodeSet()); } if ( node instanceof TerminalNode) { // leere Menge -> nichts zu tun } else { // der Knoten enthält eine Operation OperatorNode operatorNode = (OperatorNode) node; switch ( operatorNode.getOperatorType()) { case CONCATENATION: { TreeNodeCollection lastpos = lastpos( operatorNode.getLeftChildNode()); for ( TreeNode lastposNode : lastpos) { TreeNodeCollection union = followPositions.remove( lastposNode); union = Sets.unionCollections( union, firstpos( operatorNode.getRightChildNode())); followPositions.put( lastposNode, union); } } break; case REPETITION: { TreeNodeCollection lastpos = lastpos( node); for ( TreeNode lastposNode : lastpos) { TreeNodeCollection union = followPositions.remove( lastposNode); union = Sets.unionCollections( union, firstpos( node)); followPositions.put( lastposNode, union); } } break; case ALTERNATIVE: default: // keine Reihenfolge -> nichts zu tun } } } /** * Prüft, ob der Teilbaum das Lesen des leere Wort ermöglicht. * * @param node * @return * @see <a href="http://kontext.fraunhofer.de/haenelt/kurs/Skripten/FSA-Skript/Haenelt_EA_RegEx2EA.pdf">Überführung regulärer Ausdrücke in endliche Automaten (S. 24)</a> */ private boolean nullable( TreeNode node) { if ( !nullables.containsKey( node)) nullables.put( node, calculateNullableForNode( node)); return nullables.get( node); } /** * Liefert eine Sammlung aller Knoten, die bei Worten gebildet über den * Unterbaum ab diesem Knoten an erste Stelle stehen können. * * @param node * @return * @see <a href="http://kontext.fraunhofer.de/haenelt/kurs/Skripten/FSA-Skript/Haenelt_EA_RegEx2EA.pdf">Überführung regulärer Ausdrücke in endliche Automaten (S. 25)</a> */ private TreeNodeCollection firstpos( TreeNode node) { if ( !firstPositions.containsKey( node)) firstPositions.put( node, calculateFirstposForNode( node)); return firstPositions.get( node); } /** * Liefert eine Sammlung aller Knoten, die am Ende eines Wortes stehen können, * welches über den Unterbaum den Knoten n gebildet werden können. * * @param node * @return * @see <a href="http://kontext.fraunhofer.de/haenelt/kurs/Skripten/FSA-Skript/Haenelt_EA_RegEx2EA.pdf">Überführung regulärer Ausdrücke in endliche Automaten (S. 25)</a> */ private TreeNodeCollection lastpos( TreeNode node) { if ( !lastPositions.containsKey( node)) lastPositions.put( node, calculateLastposForNode( node)); return lastPositions.get( node); } /** * Liefert eine Sammlung aller Knoten, die auf den gegebenen Knoten in einem Wort folgen können. * * @param node * @return * @throws SyntaxTreeException * @see <a href="http://kontext.fraunhofer.de/haenelt/kurs/Skripten/FSA-Skript/Haenelt_EA_RegEx2EA.pdf">Überführung regulärer Ausdrücke in endliche Automaten (S. 27)</a> */ private TreeNodeCollection followpos( TreeNode node) { calculateFollowposForNode( node); return followPositions.get( node); } private void resetFollowPositions() { followPositions.clear(); } public HashMap<TreeNode, TreeNodeCollection> getFirstPositions() { return firstPositions; } public HashMap<TreeNode, TreeNodeCollection> getFollowPositions() { return followPositions; } public HashMap<TreeNode, TreeNodeCollection> getLastPositions() { return lastPositions; } public HashMap<TreeNode, Boolean> getNullables() { return nullables; } /** * Attributiert den Operatorbaum. * @param operatorTree */ public void attributizeOperatorTree( RegexOperatorTree operatorTree) { for ( TreeNode treeNode : operatorTree) { if ( treeNode instanceof TerminalNode // filter empty dummy nodes || treeNode instanceof OperatorNode) { nullable( treeNode); firstpos( treeNode); lastpos( treeNode); } } // calc followpos resetFollowPositions(); for ( TreeNode treeNode : operatorTree) { if ( treeNode instanceof TerminalNode || treeNode instanceof OperatorNode) { followpos( treeNode); } } } }