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.process;
010:
011: import net.sourceforge.chaperon.build.Automaton;
012: import net.sourceforge.chaperon.build.ReduceAction;
013: import net.sourceforge.chaperon.build.ShiftAction;
014: import net.sourceforge.chaperon.build.State;
015: import net.sourceforge.chaperon.model.grammar.Grammar;
016: import net.sourceforge.chaperon.model.grammar.Production;
017: import net.sourceforge.chaperon.model.symbol.Symbol;
018: import net.sourceforge.chaperon.model.symbol.Terminal;
019:
020: import org.apache.commons.logging.Log;
021:
022: import org.xml.sax.Attributes;
023: import org.xml.sax.ContentHandler;
024: import org.xml.sax.Locator;
025: import org.xml.sax.SAXException;
026: import org.xml.sax.ext.LexicalHandler;
027: import org.xml.sax.helpers.AttributesImpl;
028: import org.xml.sax.helpers.LocatorImpl;
029:
030: import java.util.Stack;
031:
032: //import org.xml.sax.ext.LexicalHandler;
033:
034: /**
035: * This class represents a simulation of a pushdown automata using the parser automaton class.
036: *
037: * @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
038: * @version CVS $Id: GeneralParserProcessor.java,v 1.19 2003/12/14 09:41:35 benedikta Exp $
039: */
040: public class GeneralParserProcessor implements ContentHandler,
041: LexicalHandler {
042: private static final String NS = "http://chaperon.sourceforge.net/schema/lexemes/1.0";
043: private static final String LEXEMES = "lexemes";
044: private static final String LEXEME = "lexeme";
045:
046: /** Namespace for the generated SAX events. */
047: private static final String NS_OUTPUT = "http://chaperon.sourceforge.net/schema/syntaxtree/1.0";
048: private static final String OUTPUT = "output";
049: private ContentHandler contentHandler = null;
050: private LexicalHandler lexicalHandler = null;
051: private Locator locator = null;
052: private LocatorImpl locatorImpl = null;
053: private static final int STATE_OUTER = 0;
054: private static final int STATE_LEXEMES = 1;
055: private static final int STATE_LEXEME = 2;
056: private int state = STATE_OUTER;
057: private Automaton automaton;
058: private Grammar grammar;
059: private boolean flatten = false;
060:
061: //private ParserHandler handler;
062: private Stack current = new Stack();
063: private Stack next = new Stack();
064: private Log log;
065:
066: //private boolean recovery = false;
067: private int maxActiveStates = 50;
068:
069: /**
070: * Create a new parser processor.
071: */
072: public GeneralParserProcessor() {
073: }
074:
075: /**
076: * Create a new parser processor.
077: *
078: * @param automaton Parser automaton, which the processor should ues.
079: * @param handler Handler, which should receives the parser events.
080: * @param log Log, which should used.
081: */
082: public GeneralParserProcessor(Automaton automaton, Log log) {
083: this .automaton = automaton;
084: this .log = log;
085: }
086:
087: /**
088: * Set the parser automaton for the processor.
089: *
090: * @param automaton Parser automaton.
091: */
092: public void setParserAutomaton(Automaton automaton) {
093: this .automaton = automaton;
094: this .grammar = automaton.getGrammar();
095: }
096:
097: /**
098: * Set the <code>ContentHandler</code> that will receive XML data.
099: */
100: public void setContentHandler(ContentHandler handler) {
101: this .contentHandler = handler;
102: }
103:
104: /**
105: * Set the <code>LexicalHandler</code> that will receive XML data.
106: */
107: public void setLexicalHandler(LexicalHandler handler) {
108: this .lexicalHandler = handler;
109: }
110:
111: /**
112: * Provide processor with a log.
113: *
114: * @param log The log.
115: */
116: public void setLog(Log log) {
117: this .log = log;
118: }
119:
120: /**
121: * If the adapter should produce a more flatten XML hirachy, which means elements which the same
122: * name will be collapsed
123: *
124: * @param flatten True, if a more flatten hirachy should be produced.
125: */
126: public void setFlatten(boolean flatten) {
127: this .flatten = flatten;
128: }
129:
130: /**
131: * Receive an object for locating the origin of SAX document events.
132: *
133: * @param locator
134: */
135: public void setDocumentLocator(Locator locator) {
136: this .locator = locator;
137:
138: if (locator != null) {
139: this .locatorImpl = new LocatorImpl(locator);
140: contentHandler.setDocumentLocator(locatorImpl);
141: }
142: }
143:
144: /**
145: * Receive notification of the beginning of a document.
146: *
147: * @throws SAXException
148: */
149: public void startDocument() throws SAXException {
150: locatorImpl.setLineNumber(locator.getLineNumber());
151: locatorImpl.setColumnNumber(locator.getColumnNumber());
152: contentHandler.startDocument();
153: state = STATE_OUTER;
154: }
155:
156: /**
157: * Receive notification of the beginning of an element.
158: *
159: * @param namespaceURI
160: * @param localName
161: * @param qName
162: * @param atts
163: *
164: * @throws SAXException
165: */
166: public void startElement(String namespaceURI, String localName,
167: String qName, Attributes atts) throws SAXException {
168: locatorImpl.setLineNumber(locator.getLineNumber());
169: locatorImpl.setColumnNumber(locator.getColumnNumber());
170:
171: if (state == STATE_OUTER) {
172: if ((namespaceURI != null) && (namespaceURI.equals(NS))
173: && (localName.equals(LEXEMES))) {
174: processStartDocument();
175: state = STATE_LEXEMES;
176: } else
177: contentHandler.startElement(namespaceURI, localName,
178: qName, atts);
179: } else if (state == STATE_LEXEMES) {
180: if ((namespaceURI != null) && (namespaceURI.equals(NS))
181: && (localName.equals(LEXEME))) {
182: processLexeme(atts.getValue("symbol"), atts
183: .getValue("text"));
184: state = STATE_LEXEME;
185: } else
186: throw new SAXException("Unexpected start element.");
187: } else if (state == STATE_LEXEME)
188: throw new SAXException("Unexpected start element.");
189: }
190:
191: /**
192: * Receive notification of the end of an element.
193: *
194: * @param namespaceURI
195: * @param localName
196: * @param qName
197: *
198: * @throws SAXException
199: */
200: public void endElement(String namespaceURI, String localName,
201: String qName) throws SAXException {
202: locatorImpl.setLineNumber(locator.getLineNumber());
203: locatorImpl.setColumnNumber(locator.getColumnNumber());
204:
205: if (state == STATE_OUTER)
206: contentHandler.endElement(namespaceURI, localName, qName);
207: else if (state == STATE_LEXEMES) {
208: if ((namespaceURI != null) && (namespaceURI.equals(NS))
209: && (localName.equals(LEXEMES))) {
210: contentHandler.startPrefixMapping("", NS_OUTPUT);
211: contentHandler.startElement(NS_OUTPUT, OUTPUT, OUTPUT,
212: new AttributesImpl());
213:
214: processEndDocument();
215:
216: contentHandler.endElement(NS_OUTPUT, OUTPUT, OUTPUT);
217: contentHandler.endPrefixMapping("");
218:
219: state = STATE_OUTER;
220: } else
221: throw new SAXException("Unexpected end element.");
222: } else if (state == STATE_LEXEME)
223: state = STATE_LEXEMES;
224: }
225:
226: /**
227: * Receive notification of character data.
228: *
229: * @param ch
230: * @param start
231: * @param length
232: *
233: * @throws SAXException
234: */
235: public void characters(char[] ch, int start, int length)
236: throws SAXException {
237: locatorImpl.setLineNumber(locator.getLineNumber());
238: locatorImpl.setColumnNumber(locator.getColumnNumber());
239:
240: if (state == STATE_OUTER)
241: contentHandler.characters(ch, start, length);
242: }
243:
244: /**
245: * Receive notification of ignorable whitespace in element content.
246: *
247: * @param ch
248: * @param start
249: * @param length
250: *
251: * @throws SAXException
252: */
253: public void ignorableWhitespace(char[] ch, int start, int length)
254: throws SAXException {
255: locatorImpl.setLineNumber(locator.getLineNumber());
256: locatorImpl.setColumnNumber(locator.getColumnNumber());
257:
258: if (state == STATE_OUTER)
259: contentHandler.ignorableWhitespace(ch, start, length);
260: }
261:
262: /**
263: * Begin the scope of a prefix-URI Namespace mapping.
264: *
265: * @param prefix
266: * @param uri
267: *
268: * @throws SAXException
269: */
270: public void startPrefixMapping(String prefix, String uri)
271: throws SAXException {
272: locatorImpl.setLineNumber(locator.getLineNumber());
273: locatorImpl.setColumnNumber(locator.getColumnNumber());
274:
275: contentHandler.startPrefixMapping(prefix, uri);
276: }
277:
278: /**
279: * End the scope of a prefix-URI mapping.
280: *
281: * @param prefix
282: *
283: * @throws SAXException
284: */
285: public void endPrefixMapping(String prefix) throws SAXException {
286: locatorImpl.setLineNumber(locator.getLineNumber());
287: locatorImpl.setColumnNumber(locator.getColumnNumber());
288:
289: contentHandler.endPrefixMapping(prefix);
290: }
291:
292: /**
293: * Receive notification of a processing instruction.
294: *
295: * @param target
296: * @param data
297: *
298: * @throws SAXException
299: */
300: public void processingInstruction(String target, String data)
301: throws SAXException {
302: locatorImpl.setLineNumber(locator.getLineNumber());
303: locatorImpl.setColumnNumber(locator.getColumnNumber());
304:
305: if (state == STATE_OUTER)
306: contentHandler.processingInstruction(target, data);
307: }
308:
309: /**
310: * Receive notification of a skipped entity.
311: *
312: * @param name
313: *
314: * @throws SAXException
315: */
316: public void skippedEntity(String name) throws SAXException {
317: locatorImpl.setLineNumber(locator.getLineNumber());
318: locatorImpl.setColumnNumber(locator.getColumnNumber());
319:
320: if (state == STATE_OUTER)
321: contentHandler.skippedEntity(name);
322: }
323:
324: /**
325: * Receive notification of the end of a document.
326: *
327: * @throws SAXException
328: */
329: public void endDocument() throws SAXException {
330: locatorImpl.setLineNumber(locator.getLineNumber());
331: locatorImpl.setColumnNumber(locator.getColumnNumber());
332:
333: if (state == STATE_OUTER)
334: contentHandler.endDocument();
335: }
336:
337: /**
338: * Report the start of DTD declarations, if any.
339: */
340: public void startDTD(String name, String publicId, String systemId)
341: throws SAXException {
342: if (lexicalHandler != null)
343: lexicalHandler.startDTD(name, publicId, systemId);
344: }
345:
346: /**
347: * Report the end of DTD declarations.
348: */
349: public void endDTD() throws SAXException {
350: if (lexicalHandler != null)
351: lexicalHandler.endDTD();
352: }
353:
354: /**
355: * Report the beginning of an entity.
356: */
357: public void startEntity(String name) throws SAXException {
358: if (lexicalHandler != null)
359: lexicalHandler.startEntity(name);
360: }
361:
362: /**
363: * Report the end of an entity.
364: */
365: public void endEntity(String name) throws SAXException {
366: if (lexicalHandler != null)
367: lexicalHandler.endEntity(name);
368: }
369:
370: /**
371: * Report the start of a CDATA section.
372: */
373: public void startCDATA() throws SAXException {
374: if (lexicalHandler != null)
375: lexicalHandler.startCDATA();
376: }
377:
378: /**
379: * Report the end of a CDATA section.
380: */
381: public void endCDATA() throws SAXException {
382: if (lexicalHandler != null)
383: lexicalHandler.endCDATA();
384: }
385:
386: /**
387: * Report an XML comment anywhere in the document.
388: */
389: public void comment(char[] ch, int start, int len)
390: throws SAXException {
391: if (lexicalHandler != null)
392: lexicalHandler.comment(ch, start, len);
393: }
394:
395: /**
396: * Receives the notification, that the lexical processor starts reading a new document.
397: *
398: * @throws Exception If a exception occurs.
399: */
400: private void processStartDocument() {
401: current.clear();
402: current.push(new StateNode(automaton.getState(0), null, null));
403: next.clear();
404:
405: count = 0;
406:
407: System.out.println("Automaton:\n" + automaton);
408:
409: //handler.handleStartDocument();
410: }
411:
412: private static int count = 0;
413:
414: /**
415: * Receives the notification, that the lexical processor has recognized a lexeme.
416: *
417: * @param symbol Symbol of the lexeme.
418: * @param text Recognized text.
419: *
420: * @throws Exception If a exception occurs.
421: */
422: private void processLexeme(String symbolname, String text) {
423: Terminal symbol = new Terminal(symbolname);
424:
425: System.out
426: .println("\n===================================\nProcess "
427: + symbolname);
428:
429: if (current.isEmpty())
430: throw new IllegalStateException(
431: "Parsing process is aborted");
432:
433: System.out.println("Current states");
434:
435: for (int i = 0; i < current.size(); i++)
436: System.out.println(current.get(i));
437:
438: System.out.println();
439:
440: if (current.size() > maxActiveStates)
441: throw new IllegalStateException(
442: "Processor occupied too many states");
443:
444: /* ============================ Reduce =================================== */
445: int watchdog = 0;
446:
447: while (!current.isEmpty()) {
448: if (watchdog++ > 20)
449: throw new IllegalStateException("overflow");
450:
451: StateNode statenode = (StateNode) current.pop();
452:
453: next.push(statenode);
454:
455: ReduceAction[] reduceactions = statenode.state
456: .getReduceActions();
457:
458: if (reduceactions.length > 0) {
459: for (int i = 0; i < reduceactions.length; i++) {
460: Production production = reduceactions[i].production;
461:
462: if ((log != null) && (log.isDebugEnabled()))
463: log.debug(
464: /*"State "+node.state+*/
465: " reduce " + production.getSymbol());
466:
467: /*+
468: " ("+production+")");*/
469: ProductionNode productionnode = new ProductionNode(
470: production);
471: TreeNode[] descendants = new TreeNode[production
472: .getDefinition().getSymbolCount()];
473:
474: StateNode ancestor = statenode;
475:
476: for (int j = production.getDefinition()
477: .getSymbolCount() - 1; j >= 0; j--) {
478: descendants[j] = ancestor.treenode;
479: ancestor = ancestor.ancestor;
480: }
481:
482: productionnode.descendants = descendants;
483:
484: if (descendants.length > 0) {
485: productionnode.linenumber = descendants[0].linenumber;
486: productionnode.columnnumber = descendants[0].columnnumber;
487: } else {
488: productionnode.linenumber = locator
489: .getLineNumber();
490: productionnode.columnnumber = locator
491: .getColumnNumber();
492: }
493:
494: ShiftAction shiftaction = ancestor.state
495: .getShiftAction(productionnode.symbol);
496:
497: if (shiftaction != null) {
498: StateNode newstatenode = getStateNode(current,
499: shiftaction.state, ancestor);
500:
501: if (newstatenode == null) {
502: System.out
503: .println("new state node: new state="
504: + automaton
505: .indexOf(shiftaction.state)
506: + " ancestor state="
507: + automaton
508: .indexOf(ancestor.state));
509: newstatenode = new StateNode(
510: shiftaction.state, ancestor,
511: productionnode);
512: current.push(newstatenode);
513: } else {
514: System.out.println("merging state node");
515:
516: ProductionNode oldproductionnode = (ProductionNode) newstatenode.treenode;
517:
518: if (grammar
519: .getPriority(oldproductionnode.production) > grammar
520: .getPriority(production)) {
521: System.out.println("priority("
522: + production + ") < priority("
523: + oldproductionnode.production
524: + ")");
525: newstatenode.treenode = productionnode;
526: } else
527: System.out.println("priority("
528: + production + ") >= priority("
529: + oldproductionnode.production
530: + ")");
531: }
532: }
533: }
534: }
535: }
536:
537: Stack dummy = next;
538: next = current;
539: current = dummy;
540:
541: System.out.println("Current states");
542:
543: for (int i = 0; i < current.size(); i++)
544: System.out.println(current.get(i));
545:
546: System.out.println();
547:
548: /* ==================================== Shift =================================== */
549: TokenNode tokennode = new TokenNode(symbol, text);
550:
551: if (locator != null) {
552: tokennode.linenumber = locator.getLineNumber();
553: tokennode.columnnumber = locator.getColumnNumber();
554: }
555:
556: while (!current.isEmpty()) {
557: StateNode statenode = (StateNode) current.pop();
558:
559: ShiftAction shiftaction = statenode.state
560: .getShiftAction(symbol);
561:
562: if (shiftaction != null) {
563: if ((log != null) && (log.isDebugEnabled()))
564: log.debug(
565: /*"State "+state+*/
566: " shift token " + symbolname + " (" + symbol + ")");
567:
568: next.push(new StateNode(shiftaction.state, statenode,
569: tokennode));
570: }
571: }
572:
573: if (next.isEmpty())
574: throw new IllegalArgumentException("Token " + symbolname
575: + " is not expected in this state");
576:
577: dummy = next;
578: next = current;
579: current = dummy;
580:
581: System.out.println("Current states");
582:
583: for (int i = 0; i < current.size(); i++)
584: System.out.println(current.get(i));
585:
586: System.out.println();
587: }
588:
589: /**
590: * Receives the notification, that the lexical processor accepted the complete document, and
591: * stops with reading.
592: *
593: * @throws Exception If a exception occurs.
594: */
595: private void processEndDocument() throws SAXException {
596: System.out
597: .println("\n===================================\nProcess EOF");
598:
599: while (!current.isEmpty()) {
600: StateNode statenode = (StateNode) current.pop();
601:
602: ReduceAction[] reduceactions = statenode.state
603: .getReduceActions();
604:
605: if (reduceactions.length > 0) {
606: for (int i = 0; i < reduceactions.length; i++) {
607: Production production = reduceactions[i].production;
608:
609: ProductionNode productionnode = new ProductionNode(
610: production);
611: TreeNode[] descendants = new TreeNode[production
612: .getDefinition().getSymbolCount()];
613:
614: StateNode ancestor = statenode;
615:
616: for (int j = production.getDefinition()
617: .getSymbolCount() - 1; j >= 0; j--) {
618: descendants[j] = ancestor.treenode;
619: ancestor = ancestor.ancestor;
620: }
621:
622: productionnode.descendants = descendants;
623:
624: ShiftAction shiftaction = ancestor.state
625: .getShiftAction(productionnode.symbol);
626:
627: //System.out.println("current state:\n"+ancestor.state+"\ntransition for "+productionnode.symbol+" = "+shiftaction);
628: if ((automaton.getState(0) == ancestor.state)
629: && (productionnode.symbol.equals(grammar
630: .getStartSymbol()))) {
631: if ((log != null) && (log.isDebugEnabled()))
632: log.debug("State " + state + " accept");
633:
634: StateNode newstatenode = getStateNode(next,
635: null, ancestor);
636:
637: if (newstatenode == null) {
638: newstatenode = new StateNode(null,
639: ancestor, productionnode);
640: next.push(newstatenode);
641: } else {
642: System.out.println("merging state node");
643:
644: ProductionNode oldproductionnode = (ProductionNode) newstatenode.treenode;
645:
646: if (grammar
647: .getPriority(oldproductionnode.production) > grammar
648: .getPriority(production)) {
649: System.out.println("priority("
650: + production + ") < priority("
651: + oldproductionnode.production
652: + ")");
653: newstatenode.treenode = productionnode;
654: } else
655: System.out.println("priority("
656: + production + ") >= priority("
657: + oldproductionnode.production
658: + ")");
659: }
660: } else {
661: if ((log != null) && (log.isDebugEnabled()))
662: log.debug(
663: /*"State "+node.state+*/
664: " reduce " + production.getSymbol() + " ("
665: + production + ")");
666:
667: /* StateNode newstatenode = new
668: StateNode(ancestor.state.getShiftAction(productionnode.symbol).state, ancestor, productionnode);
669:
670: current.push(newstatenode);*/
671: StateNode newstatenode = getStateNode(current,
672: shiftaction.state, ancestor);
673:
674: if (newstatenode == null) {
675: newstatenode = new StateNode(
676: shiftaction.state, ancestor,
677: productionnode);
678: current.push(newstatenode);
679: } else {
680: System.out.println("merging state node");
681:
682: ProductionNode oldproductionnode = (ProductionNode) newstatenode.treenode;
683:
684: if (grammar
685: .getPriority(oldproductionnode.production) > grammar
686: .getPriority(production)) {
687: System.out.println("priority("
688: + production + ") < priority("
689: + oldproductionnode.production
690: + ")");
691: newstatenode.treenode = productionnode;
692: } else
693: System.out.println("priority("
694: + production + ") >= priority("
695: + oldproductionnode.production
696: + ")");
697: }
698: }
699:
700: System.out.println("Current states");
701:
702: for (int k = 0; k < current.size(); k++)
703: System.out.println(current.get(k));
704:
705: System.out.println();
706: }
707: }
708: }
709:
710: if (log.isDebugEnabled())
711: log.debug("Parser found " + next.size() + " alternatives");
712:
713: System.out.println();
714:
715: int index = 1;
716:
717: while (!next.isEmpty()) {
718: StateNode state = (StateNode) next.pop();
719:
720: //System.out.println(index+". result: "+((StateNode)next.pop()).treenode);
721: fireEvents(null, state.treenode);
722: index++;
723: }
724:
725: if (next.size() > 1)
726: log.warn("Grammar is ambig, found " + next.size()
727: + " alternative trees");
728: }
729:
730: private StateNode getStateNode(Stack stack, State state,
731: StateNode ancestor) {
732: StateNode statenode = null;
733:
734: for (int j = 0; j < stack.size(); j++) {
735: statenode = (StateNode) stack.get(j);
736:
737: if ((statenode.ancestor == ancestor)
738: && (statenode.state == state))
739: return statenode;
740: }
741:
742: return null;
743: }
744:
745: /**
746: * Fire the SAX events by traverseing the hirachy.
747: *
748: * @param parent Parent node.
749: * @param node Current node.
750: *
751: * @throws Exception If an exception occurs.
752: */
753: private void fireEvents(ProductionNode parent, TreeNode node)
754: throws SAXException {
755: if (node instanceof ProductionNode) {
756: ProductionNode production = (ProductionNode) node;
757:
758: if (locatorImpl != null) {
759: locatorImpl.setLineNumber(production.linenumber);
760: locatorImpl.setColumnNumber(production.columnnumber);
761: }
762:
763: if ((!flatten) || (parent == null)
764: || (!parent.symbol.equals(production.symbol)))
765: contentHandler.startElement(NS_OUTPUT,
766: production.symbol.getName(), production.symbol
767: .getName(), new AttributesImpl());
768:
769: for (int i = 0; i < production.descendants.length; i++)
770: fireEvents(production, production.descendants[i]);
771:
772: if ((!flatten) || (parent == null)
773: || (!parent.symbol.equals(production.symbol)))
774: contentHandler.endElement(NS_OUTPUT, production.symbol
775: .getName(), production.symbol.getName());
776: } else {
777: TokenNode token = (TokenNode) node;
778:
779: if (locatorImpl != null) {
780: locatorImpl.setLineNumber(token.linenumber);
781: locatorImpl.setColumnNumber(token.columnnumber);
782: }
783:
784: contentHandler.startElement(NS_OUTPUT, token.symbol
785: .getName(), token.symbol.getName(),
786: new AttributesImpl());
787: contentHandler.characters(token.text.toCharArray(), 0,
788: token.text.length());
789: contentHandler.endElement(NS_OUTPUT,
790: token.symbol.getName(), token.symbol.getName());
791: }
792: }
793:
794: private class StateNode {
795: public StateNode(State state, StateNode ancestor,
796: TreeNode treenode) {
797: this .state = state;
798: this .treenode = treenode;
799: this .ancestor = ancestor;
800: }
801:
802: public State state = null;
803: public StateNode ancestor = null;
804: public TreeNode treenode = null;
805:
806: public String toString() {
807: StringBuffer buffer = new StringBuffer();
808:
809: if (ancestor != null) {
810: buffer.append(ancestor.toString());
811: buffer.append(" <- ");
812: }
813:
814: buffer.append("<");
815: buffer.append(automaton.indexOf(state));
816:
817: /*buffer.append(",");
818: if (ancestor!=null)
819: buffer.append(automaton.indexOf(ancestor.state));*/
820: buffer.append(">");
821:
822: return buffer.toString();
823: }
824: }
825:
826: private abstract class TreeNode {
827: public Symbol symbol = null;
828: public int linenumber = 1;
829: public int columnnumber = 1;
830: }
831:
832: private class TokenNode extends TreeNode {
833: public TokenNode(Terminal symbol, String text) {
834: this .symbol = symbol;
835: this .text = text;
836: }
837:
838: public String text = null;
839:
840: public String toString() {
841: StringBuffer buffer = new StringBuffer();
842:
843: buffer.append("{");
844: buffer.append(symbol);
845: buffer.append(":");
846: buffer.append(text);
847: buffer.append("}");
848:
849: return buffer.toString();
850: }
851: }
852:
853: private class ProductionNode extends TreeNode {
854: /*public ProductionNode(Nonterminal symbol)
855: {
856: this.symbol = symbol;
857: }*/
858: public ProductionNode(Production production) {
859: this .production = production;
860: this .symbol = production.getSymbol();
861: }
862:
863: public Production production = null;
864: public TreeNode[] descendants = null;
865:
866: public String toString() {
867: StringBuffer buffer = new StringBuffer();
868:
869: buffer.append("{");
870: buffer.append(symbol);
871: buffer.append(":");
872:
873: for (int i = 0; i < descendants.length; i++)
874: buffer.append(descendants[i].toString());
875:
876: buffer.append("}");
877:
878: return buffer.toString();
879: }
880: }
881: }
|