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 net.sourceforge.chaperon.common.IntegerList;
012: import net.sourceforge.chaperon.model.Violations;
013: import net.sourceforge.chaperon.model.symbol.Nonterminal;
014: import net.sourceforge.chaperon.model.symbol.Symbol;
015: import net.sourceforge.chaperon.model.symbol.SymbolList;
016: import net.sourceforge.chaperon.model.symbol.SymbolSet;
017: import net.sourceforge.chaperon.model.symbol.Terminal;
018:
019: import java.io.Serializable;
020:
021: import java.util.Enumeration;
022: import java.util.Hashtable;
023: import java.util.Vector;
024:
025: /**
026: * This class represents a model for a grammar. The content of the grammar includes the
027: * productions, start symbol, associativities and priorities.
028: *
029: * @author <a href="mailto:stephan@apache.org">Stephan Michels </a>
030: * @version CVS $Id: Grammar.java,v 1.16 2003/12/09 19:55:52 benedikta Exp $
031: */
032: public class Grammar implements Serializable, Cloneable {
033: // Start symbol
034: private Nonterminal startsymbol = null;
035:
036: // Productions
037: private Vector productions = new Vector();
038: private Hashtable priorities = new Hashtable();
039: private Hashtable associativities = new Hashtable();
040: private String location = null;
041:
042: /**
043: * Creates an empty grammar.
044: */
045: public Grammar() {
046: }
047:
048: /**
049: * Add a production to this grammar.
050: *
051: * @param production Production, which should be added.
052: *
053: * @return Index of the production in this grammar.
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 to this grammar.
065: *
066: * @param list Array of productions.
067: */
068: public void addProduction(Production[] list) {
069: for (int i = 0; i < list.length; i++)
070: addProduction((Production) list[i].clone());
071: }
072:
073: /**
074: * Removes a production by an index from this grammar.
075: *
076: * @param index Index of the production, which should be removed.
077: */
078: public void removeProduction(int index) {
079: productions.removeElementAt(index);
080: }
081:
082: /**
083: * Replace a production by an index in this grammar.
084: *
085: * @param index The index, at which the productionshould be replaced.
086: * @param production The production.
087: */
088: public void setProduction(int index, Production production) {
089: if ((index < 0) || (index > productions.size()))
090: throw new IndexOutOfBoundsException();
091:
092: productions.setElementAt(production, index);
093: }
094:
095: /**
096: * Return a production giving by an index.
097: *
098: * @param index Index of the Production.
099: *
100: * @return The production.
101: */
102: public Production getProduction(int index) {
103: /* if ((index<0) || (index>productions.size()))
104: throw new IndexOutOfBoundsException(index);*/
105: return (Production) productions.elementAt(index);
106: }
107:
108: /**
109: * Returns all production for given nonterminal symbol as a list of indices.
110: *
111: * @param ntsymbol Nonterminal symbol
112: *
113: * @return List of indices from the productions
114: */
115: public IntegerList getProductionList(Symbol ntsymbol) {
116: IntegerList list = new IntegerList();
117: int i;
118:
119: for (i = 0; i < getProductionCount(); i++) {
120: if (getProduction(i).getSymbol().equals(ntsymbol))
121: list.add(i);
122: }
123:
124: return list;
125: }
126:
127: public Production[] getProductions(Symbol ntsymbol) {
128: int count = 0;
129: for (int i = 0; i < getProductionCount(); i++)
130: if (getProduction(i).getSymbol().equals(ntsymbol))
131: count++;
132:
133: Production[] productions = new Production[count];
134: for (int i = 0; i < getProductionCount(); i++)
135: if (getProduction(i).getSymbol().equals(ntsymbol))
136: productions[--count] = getProduction(i);
137:
138: return productions;
139: }
140:
141: /**
142: * Returns the count of productions in this grammar.
143: *
144: * @return Count of productions.
145: */
146: public int getProductionCount() {
147: return productions.size();
148: }
149:
150: /**
151: * Return the index of a production.
152: *
153: * @param production The production.
154: *
155: * @return Index of the Production.
156: */
157: public int indexOf(Production production) {
158: for (int i = 0; i < productions.size(); i++)
159: if (((Production) productions.elementAt(i))
160: .equals(production))
161: return i;
162:
163: return -1;
164: }
165:
166: /**
167: * Return the index of the next production, which found by a nonterminal symbol.
168: *
169: * @param ntsymbol Nonterminal symbol
170: *
171: * @return Index of the production.
172: */
173: public int indexOf(Symbol ntsymbol) {
174: for (int i = 0; i < productions.size(); i++)
175: if (((Production) productions.elementAt(i)).getSymbol()
176: .equals(ntsymbol))
177: return i;
178:
179: return -1;
180: }
181:
182: /**
183: * If the grammar contains a production.
184: *
185: * @param production The production, which the grammar should contain.
186: *
187: * @return True, if the grammar contains the production.
188: */
189: public boolean contains(Production production) {
190: return (indexOf(production) != -1);
191: }
192:
193: /**
194: * If the grammar contains a production with this nonterminal symbol.
195: *
196: * @param ntsymbol Nonterminal symbol.
197: *
198: * @return True, if the grammar contains a production with the symbol.
199: */
200: public boolean contains(Symbol ntsymbol) {
201: return (indexOf(ntsymbol) != -1);
202: }
203:
204: /**
205: * Removes all productions from this grammar.
206: */
207: public void removeAllProduction() {
208: productions.removeAllElements();
209: }
210:
211: /**
212: * Returns the production, which this grammar contains, as an array.
213: *
214: * @return Array if productions.
215: */
216: public Production[] getProduction() {
217: int size = productions.size();
218: Production[] mArray = new Production[size];
219:
220: for (int index = 0; index < size; index++)
221: mArray[index] = (Production) productions.elementAt(index);
222:
223: return mArray;
224: }
225:
226: /**
227: * Replace the productions of this grammar by an array of productions.
228: *
229: * @param productionArray Array of productions.
230: */
231: public void setProduction(Production[] productionArray) {
232: productions.removeAllElements();
233: for (int i = 0; i < productionArray.length; i++)
234: productions.addElement(productionArray[i]);
235: }
236:
237: /**
238: * Set a priority of a terminal symbol.
239: *
240: * @param terminal Terminal symbol.
241: * @param priority Priority of the symbol.
242: */
243: public void setPriority(Terminal terminal, int priority) {
244: if (terminal == null)
245: throw new NullPointerException();
246:
247: priorities.put(terminal, new Integer(priority));
248: }
249:
250: /**
251: * Returns the priority of a terminal symbol.
252: *
253: * @param terminal Terminal symbol.
254: *
255: * @return Priority of the symbol.
256: */
257: public int getPriority(Terminal terminal) {
258: Integer priority = (Integer) priorities.get(terminal);
259:
260: if (priority == null)
261: return 0;
262:
263: return priority.intValue();
264: }
265:
266: /**
267: * Return the priority of a production in this grammar.
268: *
269: * @param production Production.
270: *
271: * @return Priority of the production.
272: */
273: public int getPriority(Production production) {
274: if (!contains(production))
275: return 0;
276:
277: if (production.getPrecedence() != null)
278: return getPriority(production.getPrecedence());
279:
280: SymbolList definition = production.getDefinition();
281:
282: for (int i = definition.getSymbolCount() - 1; i >= 0; i--)
283: if (definition.getSymbol(i) instanceof Terminal) {
284: int priority = getPriority((Terminal) definition
285: .getSymbol(i));
286:
287: return priority;
288: }
289:
290: //return getProductionCount()-indexOf(production);
291: return 0;
292: }
293:
294: /**
295: * Set the associativity of a terminal symbol.
296: *
297: * @param terminal Terminal symbol.
298: * @param assoc Associativity of the symbol.
299: */
300: public void setAssociativity(Terminal terminal, Associativity assoc) {
301: if (terminal == null)
302: throw new NullPointerException();
303:
304: associativities.put(terminal, assoc);
305: }
306:
307: /**
308: * Return the associativity of a terminal symbol.
309: *
310: * @param terminal Terminal symbol.
311: *
312: * @return Associativity of the symbol.
313: */
314: public Associativity getAssociativity(Terminal terminal) {
315: Associativity assoc = (Associativity) associativities
316: .get(terminal);
317:
318: if (assoc == null)
319: return Associativity.NONASSOC;
320:
321: return assoc;
322: }
323:
324: /**
325: * Return the associativity of a production in this grammar.
326: *
327: * @param production Production.
328: *
329: * @return Associativity of the production.
330: */
331: public Associativity getAssociativity(Production production) {
332: if (!contains(production))
333: return Associativity.NONASSOC;
334:
335: if (production.getPrecedence() != null)
336: return getAssociativity(production.getPrecedence());
337:
338: SymbolList definition = production.getDefinition();
339:
340: for (int i = definition.getSymbolCount() - 1; i >= 0; i--)
341: if (definition.getSymbol(i) instanceof Terminal)
342: return getAssociativity((Terminal) definition
343: .getSymbol(i));
344:
345: return Associativity.NONASSOC;
346: }
347:
348: /**
349: * Return all used symbol in this grammar.
350: *
351: * @return Set of symbols, which were used.
352: */
353: public SymbolSet getSymbols() {
354: SymbolSet set = new SymbolSet();
355:
356: for (int i = 0; i < getProductionCount(); i++)
357: set.addSymbol(getProduction(i).getSymbols());
358:
359: return set;
360: }
361:
362: /**
363: * Set the start symbol for this grammar.
364: *
365: * @param startsymbol Start symbol.
366: */
367: public void setStartSymbol(Nonterminal startsymbol) {
368: this .startsymbol = startsymbol;
369: }
370:
371: /**
372: * Return the start symbol.
373: *
374: * @return Start symbol.
375: */
376: public Nonterminal getStartSymbol() {
377: return startsymbol;
378: }
379:
380: /**
381: * Set the location from the input source.
382: *
383: * @param location Location in the input source.
384: */
385: public void setLocation(String location) {
386: this .location = location;
387: }
388:
389: /**
390: * Returns the location from the input source.
391: *
392: * @return Location in the input source.
393: */
394: public String getLocation() {
395: return location;
396: }
397:
398: /**
399: * Validated the grammar.
400: *
401: * @return Return a list of violations, if this object isn't valid.
402: */
403: public Violations validate() {
404: Violations violations = new Violations();
405:
406: if (startsymbol == null)
407: violations.addViolation("Start symbol is not defined",
408: location);
409: else if (!contains(startsymbol))
410: violations.addViolation("Start symbol \"" + startsymbol
411: + "\"" + "is not defined through a production",
412: location);
413:
414: if (getProductionCount() <= 0)
415: violations.addViolation("No productions are defined",
416: location);
417:
418: for (Enumeration e = productions.elements(); e
419: .hasMoreElements();)
420: violations.addViolations(((Production) e.nextElement())
421: .validate());
422:
423: SymbolSet ntsymbols = getSymbols().getNonterminals();
424:
425: for (int i = 0; i < ntsymbols.getSymbolCount(); i++) {
426: if (!contains(ntsymbols.getSymbol(i)))
427: violations.addViolation("Nonterminal symbol \""
428: + ntsymbols.getSymbol(i) + "\""
429: + "is not defined through a production",
430: location);
431:
432: if (ntsymbols.getSymbol(i).getName().equals("error"))
433: violations
434: .addViolation(
435: "Nonterminal symbol with name \"error\" is not allowed",
436: location);
437: }
438:
439: SymbolSet tsymbols = getSymbols().getTerminals();
440:
441: for (int i = 0; i < tsymbols.getSymbolCount(); i++) {
442: if ((!(tsymbols.getSymbol(i) instanceof Error))
443: && (tsymbols.getSymbol(i).getName().equals("error")))
444: violations
445: .addViolation(
446: "Terminal symbol with name \"error\" is not allowed",
447: location);
448: }
449:
450: return violations;
451: }
452:
453: /**
454: * Return a string representation of the grammar.
455: *
456: * @return String representation.
457: */
458: public String toString() {
459: StringBuffer buffer = new StringBuffer();
460:
461: buffer.append("Terminal symbols:\n");
462:
463: SymbolSet tsymbols = getSymbols().getTerminals();
464:
465: for (int i = 0; i < tsymbols.getSymbolCount(); i++) {
466: buffer.append(String.valueOf(i));
467: buffer.append(".Terminal: ");
468: buffer.append(tsymbols.getSymbol(i));
469: buffer.append(" Priority=");
470: buffer.append(String
471: .valueOf(getPriority((Terminal) tsymbols
472: .getSymbol(i))));
473: buffer.append(" Associativity=");
474: buffer.append(String
475: .valueOf(getAssociativity((Terminal) tsymbols
476: .getSymbol(i))));
477: buffer.append("\n");
478: }
479:
480: buffer.append("Produktions:\n");
481: for (int i = 0; i < getProductionCount(); i++) {
482: buffer.append(String.valueOf(i));
483: buffer.append(".Production: ");
484: buffer.append(getProduction(i).toString());
485: buffer.append("\n");
486: }
487:
488: buffer.append("\n");
489:
490: return buffer.toString();
491: }
492:
493: /**
494: * Creates a clone of this grammar.
495: *
496: * @return Clone of this grammar.
497: *
498: * @throws CloneNotSupportedException If an exception occurs during the cloning.
499: */
500: public Object clone() {
501: Grammar clone = new Grammar();
502:
503: clone.startsymbol = startsymbol;
504: for (int i = 0; i < productions.size(); i++)
505: clone.addProduction((Production) ((Production) productions
506: .elementAt(i)).clone());
507:
508: clone.location = location;
509:
510: return clone;
511: }
512: }
|