/* * Copyright (C) 2008 Universidade Federal de Campina Grande * * This file is part of OurGrid. * * OurGrid is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.ourgrid.common.specification.grammar; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Set; import java.util.Stack; import java.util.TreeMap; import org.ourgrid.common.specification.IgnoreCaseComparator; /** * From Project: Caymman(DSC/UFCG) Description: This class represents a LL(1) * grammar. * * @version 1.0 Created on Sep 25, 2003 * ************************************************************ * @version 2.0 Updated on May 23, 2004 */ public class CommonGrammar implements Grammar { /* The struct that will contain all the rules of the grammar. */ private LinkedHashMap<Integer,Rule> rules; /* The struct that will contain all the symbols of the grammar. */ private TreeMap<String,Symbol> symbols; /* The initial symbol of this grammar */ private Symbol initialSymbol; /* The syntatic table used to the grammar. */ private Rule[ ][ ] syntacticTable; /* * A stack used to check if will happen infinit loop at follows search * * @see this.follow(Symbol) */ private Stack<Rule> recursiveFollows; /** * @see org.ourgrid.common.specification.grammar.Grammar */ public CommonGrammar() { rules = new LinkedHashMap<Integer,Rule>(); symbols = new TreeMap<String,Symbol>( new IgnoreCaseComparator() ); initialSymbol = null; syntacticTable = null; recursiveFollows = new Stack<Rule>(); } /** * @see org.ourgrid.common.specification.grammar.Grammar#addSymbol(Symbol) */ public void addSymbol( Symbol symbol ) { symbols.put( symbol.getValue(), symbol ); if ( (initialSymbol == null) && (symbol.isNonTerminal()) ) { initialSymbol = symbol; } } /** * @see org.ourgrid.common.specification.grammar.Grammar#addRule(Rule) */ public void addRule( Rule rule ) { rules.put( new Integer( rule.getID() ), rule ); syntacticTable = null; // Invalidating syntacticTable } /** * @see org.ourgrid.common.specification.grammar.Grammar#getSymbol(java.lang.String) */ public Symbol getSymbol( String symbolName ) { return (symbols.get( symbolName )); } /** * @see org.ourgrid.common.specification.grammar.Grammar#getRule(int) */ public Rule getRule( int id ) { return rules.get( new Integer( id ) ); } /** * @see org.ourgrid.common.specification.grammar.Grammar#getRule(Symbol,Symbol) */ public Rule getRule( Symbol stackTop, Symbol nextSymbol ) { return this.syntacticTable[stackTop.getCode()][nextSymbol.getCode()]; } /** * @see org.ourgrid.common.specification.grammar.Grammar#getInitialSymbol() */ public Symbol getInitialSymbol() { return this.initialSymbol; } /** * @see org.ourgrid.common.specification.grammar.Grammar#getEndOfSourceSymbol() */ public Symbol getEndOfSourceSymbol() { return Symbol.EOF; } /** * The code of the last terminal symbol in the grammar. * * @return The code of the last terminal. */ public int lastTerminal() { int biggerTerminalCode = 0; Iterator<Symbol> symbolsIt = symbols.values().iterator(); while ( symbolsIt.hasNext() ) { Symbol symbol = symbolsIt.next(); if ( (symbol.isTerminal()) && (symbol.getCode() > biggerTerminalCode) ) biggerTerminalCode = symbol.getCode(); } return biggerTerminalCode; } /** * The code of the last non terminal symbol in the grammar. * * @return The code of the last non Terminal. */ public int lastNonTerminal() { int biggerNonTerminalCode = 0; Iterator<Symbol> symbolsIt = symbols.values().iterator(); while ( symbolsIt.hasNext() ) { Symbol symbol = symbolsIt.next(); if ( (symbol.isNonTerminal()) && (symbol.getCode() > biggerNonTerminalCode) ) biggerNonTerminalCode = symbol.getCode(); } return biggerNonTerminalCode; } /** * Gets all the rules of the grammar. * * @return The iterator with all the rules of the grammar. */ public Iterator<Rule> getRules() { return rules.values().iterator(); } /** * Loads the internal syntactical table using the rules and symbols * inserted. ATTENTION: Do not try to compile using this grammar without * before call this method!!! Generally it will be used after finish to read * the grammar description file. * * @see org.ourgrid.common.specification.grammar.io.GrammarReader */ public void loadSyntacticTable() { if ( syntacticTable != null ) { return; } firstCache = new HashMap<Symbol,Set<Symbol>>(); followCache = new HashMap<Symbol,Set<Symbol>>(); syntacticTable = new Rule[ lastNonTerminal() + 1 ][ lastTerminal() + 1 ]; for ( int i = 0; i < syntacticTable.length; i++ ) { for ( int j = 0; j < syntacticTable[i].length; j++ ) { syntacticTable[i][j] = null; } } Iterator<Rule> rulesIt = getRules(); while ( rulesIt.hasNext() ) { Rule rule = rulesIt.next(); Symbol head = rule.getHead(); Iterator<Symbol> firstIt = first( rule.getBody() ).iterator(); while ( firstIt.hasNext() ) { Symbol first = firstIt.next(); if ( !first.isSemanticAction() ) { if ( !first.equals( Symbol.EMPTY ) ) { syntacticTable[head.getCode()][first.getCode()] = rule; } else { Iterator<Symbol> followIt = follow( head ).iterator(); while ( followIt.hasNext() ) { Symbol follow = followIt.next(); if ( !follow.equals( Symbol.EMPTY ) ) { if ( syntacticTable[head.getCode()][follow.getCode()] == null ) { syntacticTable[head.getCode()][follow.getCode()] = rule; } } else { syntacticTable[head.getCode()][Symbol.EOF.getCode()] = rule; } } } } } } } /* * A map to caches follows of nonTerminals symbols */ private HashMap<Symbol,Set<Symbol>> followCache = new HashMap<Symbol,Set<Symbol>>(); /* * Search the follow symbols of a given symbol @param symbol the symbol to * search the follow set @return a set with all the follows to the given * symbol */ private Set<Symbol> follow( Symbol symbol ) { Set<Symbol> set = new HashSet<Symbol>(); if ( symbol.isNonTerminal() ) { if ( followCache.get( symbol ) != null ) { return followCache.get( symbol ); } if ( symbol.equals( getInitialSymbol() ) ) { set.add( Symbol.EMPTY ); } Iterator<Rule> rulesIt = getRules(); while ( rulesIt.hasNext() ) { Rule rule = rulesIt.next(); for ( int i = 0; i < rule.getBody().length; i++ ) { if ( rule.getBody()[i].equals( symbol ) ) { // rule produces // symbol Symbol nextSymbol = getNextTerminalOrNonTerminal( rule, i ); if ( (nextSymbol == null) && (!rule.getHead().equals( symbol )) && (!recursiveFollows.contains( rule )) ) { // symbol is in the end of the rule and is different // from head recursiveFollows.push( rule ); set.addAll( follow( rule.getHead() ) ); recursiveFollows.pop(); } else if ( nextSymbol != null ) { Iterator<Symbol> firstIt = first( nextSymbol ).iterator(); while ( firstIt.hasNext() ) { Symbol first = firstIt.next(); if ( !first.equals( Symbol.EMPTY ) ) { set.add( first ); } else { set.addAll( follow( nextSymbol ) ); } } } } } } followCache.put( symbol, set ); } return set; } /* * Will search for all the terminal and non terminal symbols inside the * given rule body considering a pointer to begin. @param rule the rule * where the symbols will be searched @param offset the actual position at * the body that will begin the search process @return the symbol founded of * null if any other terminal or non-terminal was found. */ private Symbol getNextTerminalOrNonTerminal( Rule rule, int offset ) { for ( int i = offset + 1; i < rule.getBody().length; i++ ) { if ( (rule.getBody()[i].isTerminal()) || (rule.getBody()[i].isNonTerminal()) ) { return rule.getBody()[i]; } } return null; } /* * A map to caches firsts of nonTerminals symbols */ private HashMap<Symbol,Set<Symbol>> firstCache = new HashMap<Symbol,Set<Symbol>>(); /* * Search the first set of symbols of a given symbol @param symbol the * symbol to search the first set @return a set with all the firsts to the * given symbol */ private Set<Symbol> first( Symbol symbol ) { Set<Symbol> set = new HashSet<Symbol>(); if ( symbol.isTerminal() ) { set.add( symbol ); } else if ( symbol.isNonTerminal() ) { if ( firstCache.get( symbol ) != null ) { return firstCache.get( symbol ); } Iterator<Rule> rulesIt = getRules(); while ( rulesIt.hasNext() ) { Rule rule = rulesIt.next(); if ( rule.getHead().equals( symbol ) ) { set.addAll( first( rule.getBody() ) ); } } firstCache.put( symbol, set ); } else { set.add( Symbol.EMPTY ); } return set; } /** * Searchs the firsts set of the symbols into the given array * * @param symbols the symbols to be searched the first symbols * @return a set with the first symbols for the given arrays's symbols */ private Set<Symbol> first( Symbol[ ] symbols ) { Set<Symbol> set = new HashSet<Symbol>(); boolean hasEmpty = true; for ( int i = 0; (hasEmpty) && (i < symbols.length); i++ ) { hasEmpty = false; Symbol nextSymbol = symbols[i]; Iterator<Symbol> firstIt = first( nextSymbol ).iterator(); while ( firstIt.hasNext() ) { Symbol first = firstIt.next(); if ( !first.equals( Symbol.EMPTY ) ) { set.add( first ); } else { hasEmpty = true; } } } if ( hasEmpty ) { set.add( Symbol.EMPTY ); } return set; } }