001: /*
002: * Copyright (C) Chaperon. All rights reserved.
003: * -------------------------------------------------------------------------
004: * This software is published under the terms of the Apache Software License
005: * version 1.1, a copy of which has been included with this distribution in
006: * the LICENSE file.
007: */
008:
009: package net.sourceforge.chaperon.model.grammar;
010:
011: import java.io.Serializable;
012: import java.util.Hashtable;
013: import java.util.Enumeration;
014: import java.util.Vector;
015:
016: import net.sourceforge.chaperon.common.IntegerList;
017: import net.sourceforge.chaperon.model.Violations;
018: import net.sourceforge.chaperon.model.symbol.Symbol;
019: import net.sourceforge.chaperon.model.symbol.SymbolList;
020: import net.sourceforge.chaperon.model.symbol.SymbolSet;
021: import net.sourceforge.chaperon.model.symbol.Nonterminal;
022: import net.sourceforge.chaperon.model.symbol.Terminal;
023:
024: /**
025: * This class contains a grammar for building a parser table
026: *
027: * @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
028: * @version CVS $Id: test3.java,v 1.3 2003/01/21 22:37:19 benedikta Exp $
029: */
030: public class Grammar implements Serializable, Cloneable {
031: // Start symbol
032: private Nonterminal startsymbol = null;
033:
034: // Productions
035: private Vector productions = new Vector();
036:
037: private Hashtable priorities = new Hashtable();
038: private Hashtable associativities = new Hashtable();
039:
040: private String location = null;
041:
042: /**
043: * Creates a empty grammar
044: */
045: public Grammar() {
046: }
047:
048: /**
049: * Add a production to this list
050: *
051: * @param production Production, which should added
052: *
053: * @return Index of the production in this list
054: */
055: public int addProduction(Production production) {
056: if (production == null)
057: throw new NullPointerException();
058:
059: productions.addElement(production);
060: return productions.size() - 1;
061: }
062:
063: /**
064: * Add a list of productions
065: *
066: * @param list Array of productions
067: */
068: public void addProduction(Production[] list) {
069: for (int i = 0; i < list.length; i++) {
070: try {
071: addProduction((Production) list[i].clone());
072: } catch (CloneNotSupportedException cnse) {
073: throw new IllegalArgumentException(
074: "Could not clone token:" + cnse.getMessage());
075: }
076: }
077: }
078:
079: /**
080: * Removes a production by an index from this list
081: *
082: * @param index Index of the production, which should be removed
083: */
084: public void removeProduction(int index) {
085: productions.removeElementAt(index);
086: }
087:
088: /**
089: * Set a production by an index
090: *
091: * @param index The index, at which the production be inserted
092: * @param production Production
093: */
094: public void setProduction(int index, Production production)
095: throws IndexOutOfBoundsException {
096: if ((index < 0) || (index > productions.size()))
097: throw new IndexOutOfBoundsException();
098: productions.setElementAt(production, index);
099: }
100:
101: /**
102: * Return a production giving by an index
103: *
104: * @param index Index of the Production
105: *
106: * @return Production
107: */
108: public Production getProduction(int index)
109: throws IndexOutOfBoundsException {
110: if ((index < 0) || (index > productions.size()))
111: throw new IndexOutOfBoundsException();
112:
113: return (Production) productions.elementAt(index);
114: }
115:
116: /**
117: * Returns all production for given non terminal symbol
118: *
119: * @param ntsymbol Non terminal symbol
120: *
121: * @return List of indices from found productions
122: */
123: public IntegerList getProductionList(Symbol ntsymbol) {
124: IntegerList list = new IntegerList();
125: int i;
126:
127: for (i = 0; i < getProductionCount(); i++) {
128: if (getProduction(i).getSymbol().equals(ntsymbol))
129: list.add(i);
130: }
131: return list;
132: }
133:
134: /**
135: * Returns the count of productions in the list
136: *
137: * @return Count of productions
138: */
139: public int getProductionCount() {
140: return productions.size();
141: }
142:
143: /**
144: * Return the index of a production
145: *
146: * @param production Production
147: *
148: * @return Index of the Production
149: */
150: public int indexOf(Production production) {
151: for (int i = 0; i < productions.size(); i++)
152: if (((Production) productions.elementAt(i))
153: .equals(production))
154: return i;
155: return -1;
156: }
157:
158: /**
159: * Return the index of the next production, which found
160: * by a non terminal symbol
161: *
162: * @param ntsymbol Non terminal symbol
163: *
164: * @return Production, which found
165: */
166: public int indexOf(Symbol ntsymbol) {
167: for (int i = 0; i < productions.size(); i++)
168: if (((Production) productions.elementAt(i)).getSymbol()
169: .equals(ntsymbol))
170: return i;
171: return -1;
172: }
173:
174: /**
175: * If the list contains a production
176: *
177: * @param production Production
178: *
179: * @return True, if the list contains the production
180: */
181: public boolean contains(Production production) {
182: return (indexOf(production) != -1);
183: }
184:
185: /**
186: * If the list contains a production with a special symbol
187: *
188: * @param ntsymbol Non terminal symbol
189: *
190: * @return True, if the list contains a production with the symbol
191: */
192: public boolean contains(Symbol ntsymbol) {
193: return (indexOf(ntsymbol) != -1);
194: }
195:
196: /**
197: * Removes all productions from this list
198: */
199: public void removeAllProduction() {
200: productions.removeAllElements();
201: }
202:
203: /**
204: * Returns a enumeration of the productions
205: * in this list.
206: *
207: * @return Enumeration of the productions
208: */
209: public Enumeration enumerateProduction() {
210: return productions.elements();
211: }
212:
213: /**
214: * Returns the content of this list
215: *
216: * @return Array of the tokens
217: */
218: public Production[] getProduction() {
219: int size = productions.size();
220: Production[] mArray = new Production[size];
221:
222: for (int index = 0; index < size; index++)
223: mArray[index] = (Production) productions.elementAt(index);
224: return mArray;
225: }
226:
227: /**
228: * Replace the content of this list by the content of an array
229: *
230: *
231: * @param productionArray
232: */
233: public void setProduction(Production[] productionArray) {
234: productions.removeAllElements();
235: for (int i = 0; i < productionArray.length; i++)
236: productions.addElement(productionArray[i]);
237: }
238:
239: public void setPriority(Terminal terminal, int priority) {
240: if (terminal == null)
241: throw new NullPointerException();
242:
243: priorities.put(terminal, new Integer(priority));
244: }
245:
246: public int getPriority(Terminal terminal) {
247: Integer priority = (Integer) priorities.get(terminal);
248: if (priority == null)
249: return 0;
250: return priority.intValue();
251: }
252:
253: public int getPriority(Production production) {
254: if (!contains(production))
255: return 0;
256:
257: if (production.getPrecedence() != null)
258: return getPriority(production.getPrecedence());
259:
260: SymbolList definition = production.getDefinition();
261: for (int i = definition.getSymbolCount() - 1; i >= 0; i--)
262: if (definition.getSymbol(i) instanceof Terminal) {
263: int priority = getPriority((Terminal) definition
264: .getSymbol(i));
265: if (priority == 0)
266: return getProductionCount() - indexOf(production);
267: return priority;
268: }
269:
270: return getProductionCount() - indexOf(production);
271: }
272:
273: public void setAssociativity(Terminal terminal, Associativity assoc) {
274: if (terminal == null)
275: throw new NullPointerException();
276:
277: associativities.put(terminal, assoc);
278: }
279:
280: public Associativity getAssociativity(Terminal terminal) {
281: Associativity assoc = (Associativity) associativities
282: .get(terminal);
283: if (assoc == null)
284: return Associativity.NONASSOC;
285: return assoc;
286: }
287:
288: public Associativity getAssociativity(Production production) {
289: if (!contains(production))
290: return Associativity.NONASSOC;
291:
292: if (production.getPrecedence() != null)
293: return getAssociativity(production.getPrecedence());
294:
295: SymbolList definition = production.getDefinition();
296: for (int i = definition.getSymbolCount() - 1; i >= 0; i--)
297: if (definition.getSymbol(i) instanceof Terminal)
298: return getAssociativity((Terminal) definition
299: .getSymbol(i));
300:
301: return Associativity.NONASSOC;
302: }
303:
304: /**
305: * Return all used symbols in this grammar
306: *
307: * @return Set of symbols
308: */
309: public SymbolSet getSymbols() {
310: SymbolSet set = new SymbolSet();
311:
312: for (int i = 0; i < getProductionCount(); i++)
313: set.addSymbol(getProduction(i).getSymbols());
314: return set;
315: }
316:
317: /**
318: * Set the a symbol a start symbol
319: *
320: * @param startsymbol
321: */
322: public void setStartSymbol(Nonterminal startsymbol) {
323: this .startsymbol = startsymbol;
324: }
325:
326: /**
327: * Returns the start symbol
328: *
329: * @return Start symbol
330: */
331: public Nonterminal getStartSymbol() {
332: return startsymbol;
333: }
334:
335: /**
336: * Set the location from the input source.
337: *
338: * @param location Location in the input source.
339: */
340: public void setLocation(String location) {
341: this .location = location;
342: }
343:
344: /**
345: * Returns the location from the input source.
346: *
347: * @return Location in the input source.
348: */
349: public String getLocation() {
350: return location;
351: }
352:
353: /**
354: * This method validates the grammer
355: *
356: * @return Return a list of violations, if this
357: * object isn't correct
358: */
359: public Violations validate() {
360: Violations violations = new Violations();
361: if (startsymbol == null)
362: violations.addViolation("Start symbol is not defined",
363: location);
364:
365: if (getProductionCount() <= 0)
366: violations.addViolation("No productions are defined",
367: location);
368:
369: for (Enumeration e = productions.elements(); e
370: .hasMoreElements();)
371: violations.addViolations(((Production) e.nextElement())
372: .validate());
373:
374: SymbolSet ntsymbols = getSymbols().getNonterminals();
375: for (int i = 0; i < ntsymbols.getSymbolCount(); i++)
376: if (!contains(ntsymbols.getSymbol(i)))
377: violations.addViolation("Nonterminal symbol \""
378: + ntsymbols.getSymbol(i) + "\""
379: + "is not defined through a production",
380: location);
381:
382: return violations;
383: }
384:
385: /*
386: *
387: *
388: * @return
389: */
390: public String toString() {
391: StringBuffer buffer = new StringBuffer();
392:
393: buffer.append(super .toString());
394: buffer.append("\n");
395:
396: buffer.append("Terminal symbols:\n");
397: SymbolSet tsymbols = getSymbols().getTerminals();
398: for (int i = 0; i < tsymbols.getSymbolCount(); i++) {
399: buffer.append(String.valueOf(i));
400: buffer.append(".Terminal: ");
401: buffer.append(tsymbols.getSymbol(i));
402: buffer.append(" Priority=");
403: buffer.append(String
404: .valueOf(getPriority((Terminal) tsymbols
405: .getSymbol(i))));
406: buffer.append(" Associativity=");
407: buffer.append(String
408: .valueOf(getAssociativity((Terminal) tsymbols
409: .getSymbol(i))));
410: buffer.append("\n");
411: }
412:
413: buffer.append("Produktions:\n");
414: for (int i = 0; i < getProductionCount(); i++) {
415: buffer.append(String.valueOf(i));
416: buffer.append(".Production: ");
417: buffer.append(getProduction(i).toString());
418: buffer.append("\n");
419: }
420:
421: buffer.append("\n");
422:
423: return buffer.toString();
424: }
425:
426: /**
427: * Creates a clone of a grammar.
428: *
429: * @return
430: *
431: * @throws CloneNotSupportedException
432: */
433: public Object clone() throws CloneNotSupportedException {
434: Grammar clone = new Grammar();
435:
436: clone.startsymbol = startsymbol;
437: for (int i = 0; i < productions.size(); i++)
438: clone.addProduction((Production) ((Production) productions
439: .elementAt(i)).clone());
440:
441: clone.location = location;
442:
443: return clone;
444: }
445: }
|