001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2004 Robert Grimm
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * as published by the Free Software Foundation; either version 2
008: * of the License, or (at your option) any later version.
009: *
010: * This program is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: * GNU General Public License for more details.
014: *
015: * You should have received a copy of the GNU General Public License
016: * along with this program; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: */
019: package xtc.parser;
020:
021: import java.util.Iterator;
022: import java.util.List;
023:
024: import xtc.Constants;
025:
026: import xtc.util.Utilities;
027:
028: import xtc.tree.Printer;
029: import xtc.tree.Visitor;
030:
031: /**
032: * The grammar pretty printer.
033: *
034: * @author Robert Grimm
035: * @version $Revision: 1.1 $
036: */
037: public class PrettyPrinter extends Visitor {
038:
039: /** Flag for whether to explicitly mark choices. */
040: public static final boolean MARK_CHOICE = true;
041:
042: /** The printer for this pretty printer. */
043: protected final Printer printer;
044:
045: /** Flag for whether the last element ended in a newline. */
046: protected boolean newline;
047:
048: /** Flag for whether the next ordered choice needs to be parenthesized. */
049: protected boolean parenChoice;
050:
051: /** Flag for whether the next sequence element needs to be parenthesized. */
052: protected boolean parenSequence;
053:
054: /**
055: * Create a new pretty printer.
056: *
057: * @param printer The printer for the new pretty printer.
058: */
059: public PrettyPrinter(Printer printer) {
060: this .printer = printer;
061: printer.register(this );
062: }
063:
064: /** Print the specified grammar. */
065: public void visit(Grammar g) {
066: // Emit header.
067: printer.sep();
068: printer.indent().p("// xtc ").pln(Constants.VERSION);
069: printer.indent().pln("// Packrat grammar pretty printer");
070: printer.sep();
071: printer.pln();
072:
073: boolean printed = false;
074:
075: // Emit package name.
076: if (null != g.pName) {
077: printed = true;
078: printer.indent().p("package ").p(g.pName).pln(';');
079: printer.pln();
080: }
081:
082: // Emit header.
083: if (null != g.header) {
084: printed = true;
085: printer.indent().p("header ");
086: g.header.accept(this );
087: printer.pln();
088: }
089:
090: // Emit class name.
091: if (!Grammar.DEFAULT_NAME.equals(g.cName)) {
092: printed = true;
093: printer.indent().p("class ").p(g.cName).pln(';');
094: printer.pln();
095: }
096:
097: // Emit main action.
098: if (null != g.body) {
099: printed = true;
100: printer.indent().p("body ");
101: g.body.accept(this );
102: printer.pln();
103: }
104:
105: // Emit footer.
106: if (null != g.footer) {
107: printed = true;
108: printer.indent().p("footer ");
109: g.footer.accept(this );
110: printer.pln();
111: }
112:
113: if (printed) {
114: printer.sep();
115: printer.pln();
116: }
117:
118: // Emit the top-level nonterminals. */
119: printer.indent().p("top ");
120: Iterator iter = g.topLevel.iterator();
121: boolean first = true;
122: while (iter.hasNext()) {
123: if (first) {
124: first = false;
125: } else {
126: printer.p(", ");
127: }
128: ((NonTerminal) iter.next()).accept(this );
129: }
130: printer.pln(';').pln();
131:
132: // Emit the productions.
133: iter = g.productions.iterator();
134: while (iter.hasNext()) {
135: ((Production) iter.next()).accept(this );
136: }
137: printer.sep().pln();
138: }
139:
140: /** Print the specified production. */
141: public void visit(Production p) {
142: if (p.hasProperty(DuplicateProductionFolder.DUPLICATES)) {
143: List sources = (List) p
144: .getProperty(DuplicateProductionFolder.DUPLICATES);
145: printer.indent().pln("/*");
146: printer.indent().p(
147: " * The following production is the result of ")
148: .pln("folding duplicates");
149: printer.indent().p(" * ").pln(Utilities.format(sources));
150: printer.indent().pln(" */");
151: }
152: printer.indent();
153: if (p.isTransient) {
154: printer.p("transient ");
155: }
156: printer.p(p.type).p(' ');
157: p.nonTerminal.accept(this );
158: printer.p(" = ");
159:
160: parenChoice = false;
161: parenSequence = false;
162:
163: p.element.accept(this );
164: printer.incr().indent().pln(';').decr();
165: printer.pln();
166: }
167:
168: /** Print the specified ordered choice. */
169: public void visit(OrderedChoice c) {
170: boolean choice = parenChoice;
171: boolean sequence = parenSequence;
172:
173: if (choice) {
174: printer.p("( ");
175: }
176: if (MARK_CHOICE) {
177: printer.p("/* Choice */ ");
178: }
179: printer.pln().incr();
180:
181: Iterator iter = c.options.iterator();
182: boolean firstOption = true;
183: while (iter.hasNext()) {
184: if (firstOption) {
185: firstOption = false;
186: printer.indent();
187:
188: } else {
189: printer.indent().p("/ ");
190: }
191:
192: parenChoice = true;
193: parenSequence = false;
194: newline = false;
195: ((Element) iter.next()).accept(this );
196:
197: if (!newline) {
198: printer.pln();
199: }
200: }
201:
202: printer.decr();
203: if (choice) {
204: printer.indent().p(')');
205: }
206:
207: parenChoice = choice;
208: parenSequence = sequence;
209: newline = false;
210: }
211:
212: /** Print the specified repetition. */
213: public void visit(Repetition r) {
214: if (newline)
215: printer.indent();
216: boolean choice = parenChoice;
217: boolean sequence = parenSequence;
218:
219: newline = false;
220: parenChoice = true;
221: parenSequence = true;
222:
223: r.element.accept(this );
224: if (r.once) {
225: printer.p('+');
226: } else {
227: printer.p('*');
228: }
229:
230: parenChoice = choice;
231: parenSequence = sequence;
232: }
233:
234: /** Print the specified option. */
235: public void visit(Option o) {
236: if (newline)
237: printer.indent();
238: boolean choice = parenChoice;
239: boolean sequence = parenSequence;
240:
241: newline = false;
242: parenChoice = true;
243: parenSequence = true;
244:
245: o.element.accept(this );
246: printer.p('?');
247:
248: parenChoice = choice;
249: parenSequence = sequence;
250: }
251:
252: /** Print the specified sequence. */
253: public void visit(Sequence s) {
254: if (newline)
255: printer.indent();
256: boolean choice = parenChoice;
257: boolean sequence = parenSequence;
258:
259: newline = false;
260: parenChoice = true;
261: parenSequence = true;
262:
263: Iterator iter = s.elements.iterator();
264: boolean first = true;
265: while (iter.hasNext()) {
266: if (first) {
267: first = false;
268: if (sequence) {
269: printer.p('(');
270: }
271: } else {
272: printer.p(' ');
273: }
274: ((Element) iter.next()).accept(this );
275: }
276: if (sequence) {
277: printer.p(')');
278: }
279:
280: parenChoice = choice;
281: parenSequence = sequence;
282: }
283:
284: /** Print the specified followed-by element. */
285: public void visit(FollowedBy p) {
286: if (newline)
287: printer.indent();
288: boolean choice = parenChoice;
289: boolean sequence = parenSequence;
290:
291: newline = false;
292: parenChoice = true;
293: parenSequence = true;
294:
295: printer.p('&');
296: p.element.accept(this );
297:
298: parenChoice = choice;
299: parenSequence = sequence;
300: }
301:
302: /** Print the specified not-followed-by element. */
303: public void visit(NotFollowedBy p) {
304: if (newline)
305: printer.indent();
306: boolean choice = parenChoice;
307: boolean sequence = parenSequence;
308:
309: newline = false;
310: parenChoice = true;
311: parenSequence = true;
312:
313: printer.p('!');
314: p.element.accept(this );
315:
316: parenChoice = choice;
317: parenSequence = sequence;
318: }
319:
320: /** Print the specified semantic predicate. */
321: public void visit(SemanticPredicate p) {
322: if (newline)
323: printer.indent();
324: boolean choice = parenChoice;
325: boolean sequence = parenSequence;
326:
327: newline = false;
328: parenChoice = true;
329: parenSequence = true;
330:
331: printer.p('&');
332: p.element.accept(this );
333:
334: parenChoice = choice;
335: parenSequence = sequence;
336: }
337:
338: /** Print the specified binding. */
339: public void visit(Binding b) {
340: if (newline)
341: printer.indent();
342: newline = false;
343: printer.p(b.name).p(':');
344: b.element.accept(this );
345: }
346:
347: /** Print the specified string match. */
348: public void visit(StringMatch m) {
349: if (newline)
350: printer.indent();
351: newline = false;
352: printer.p('\"').escape(m.text, Utilities.JAVA_ESCAPES).p("\":");
353: m.element.accept(this );
354: }
355:
356: /** Print the specified nonterminal. */
357: public void visit(NonTerminal nt) {
358: if (newline)
359: printer.indent();
360: newline = false;
361: printer.p(nt.name);
362: }
363:
364: /** Print the specified string literal. */
365: public void visit(StringLiteral l) {
366: if (newline)
367: printer.indent();
368: newline = false;
369: printer.p('\"').escape(l.text, Utilities.JAVA_ESCAPES).p('\"');
370: }
371:
372: /** Print the specified any character element. */
373: public void visit(AnyChar a) {
374: if (newline)
375: printer.indent();
376: newline = false;
377: printer.p('.');
378: }
379:
380: /** Print the specified character literal. */
381: public void visit(CharLiteral l) {
382: if (newline)
383: printer.indent();
384: newline = false;
385: printer.p('\'').escape(l.c, Utilities.JAVA_ESCAPES).p('\'');
386: }
387:
388: /** Print the specified character range. */
389: public void visit(CharRange r) {
390: if (newline)
391: printer.indent();
392: newline = false;
393: if (r.first == r.last) {
394: printer.escape(r.first, Utilities.FULL_ESCAPES);
395: } else {
396: printer.escape(r.first, Utilities.FULL_ESCAPES).p('-')
397: .escape(r.last, Utilities.FULL_ESCAPES);
398: }
399: }
400:
401: /** Print the specified character class. */
402: public void visit(CharClass c) {
403: if (newline)
404: printer.indent();
405: newline = false;
406: if (c.exclusive) {
407: printer.p("/* Exclusive */ !");
408: }
409: printer.p('[');
410: Iterator iter = c.ranges.iterator();
411: while (iter.hasNext()) {
412: ((CharRange) iter.next()).accept(this );
413: }
414: printer.p(']');
415: if (c.exclusive) {
416: printer.p(" .");
417: }
418: }
419:
420: /** Print the specified character case. */
421: public void visit(CharCase c) {
422: if (newline)
423: printer.indent();
424: newline = false;
425:
426: c.klass.accept(this );
427: printer.p(' ');
428: if (null == c.element) {
429: printer.p("&{ false }");
430: } else {
431: c.element.accept(this );
432: }
433: }
434:
435: /** Print the specified character switch. */
436: public void visit(CharSwitch s) {
437: boolean choice = parenChoice;
438: boolean sequence = parenSequence;
439:
440: printer.pln("( /* Switch */").incr();
441:
442: Iterator iter = s.cases.iterator();
443: boolean firstCase = true;
444: while (iter.hasNext()) {
445: if (firstCase) {
446: firstCase = false;
447: printer.indent();
448:
449: } else {
450: printer.indent().p("/ ");
451: }
452:
453: parenChoice = true;
454: parenSequence = false;
455: newline = false;
456: ((CharCase) iter.next()).accept(this );
457:
458: if (!newline) {
459: printer.pln();
460: }
461: }
462:
463: if (null != s.base) {
464: printer.indent().p("/ . ");
465: parenChoice = true;
466: parenSequence = false;
467: newline = false;
468: s.base.accept(this );
469:
470: if (!newline) {
471: printer.pln();
472: }
473: }
474:
475: printer.decr().indent().p(')');
476:
477: parenChoice = choice;
478: parenSequence = sequence;
479: newline = false;
480: }
481:
482: /** Print the specified action. */
483: public void visit(Action a) {
484: if (newline)
485: printer.indent();
486: if (0 == a.code.size()) {
487: // Nothing to do.
488: newline = false;
489:
490: } else if (1 == a.code.size()) {
491: newline = false;
492: printer.p("{ ").p((String) a.code.get(0)).p(" }");
493:
494: } else {
495: newline = true;
496: printer.pln('{').incr();
497: Iterator iter = a.code.iterator();
498: while (iter.hasNext()) {
499: printer.indent().pln((String) iter.next());
500: }
501: printer.decr().indent().pln('}');
502: }
503: }
504:
505: /** Print the specified null value. */
506: public void visit(NullValue v) {
507: if (newline)
508: printer.indent();
509: newline = false;
510: printer.p("/* value = null; */");
511: }
512:
513: /** Print the specified string value. */
514: public void visit(StringValue v) {
515: if (newline)
516: printer.indent();
517: if (-1 == v.text.indexOf("*/")) {
518: newline = false;
519: printer.p("/* value = \"").escape(v.text,
520: Utilities.JAVA_ESCAPES).p("\"; */");
521: } else {
522: // The string value contains the end sequence for a traditional
523: // C comment. We need to use a C++ comment.
524: newline = true;
525: printer.p("// value = \"").escape(v.text,
526: Utilities.JAVA_ESCAPES).pln("\";");
527: }
528: }
529:
530: /** Print the specified text value. */
531: public void visit(TextValue v) {
532: if (newline)
533: printer.indent();
534: newline = false;
535: printer.p("/* value = <text>; */");
536: }
537:
538: /** Print the specified empty list value. */
539: public void visit(EmptyListValue v) {
540: if (newline)
541: printer.indent();
542: newline = false;
543: printer.p("/* value = []; */");
544: }
545:
546: /** Print the specified singleton list value. */
547: public void visit(SingletonListValue v) {
548: if (newline)
549: printer.indent();
550: newline = false;
551: printer.p("/* value = [").p(v.value).p("]; */");
552: }
553:
554: /** Print the specified list value. */
555: public void visit(ListValue v) {
556: if (newline)
557: printer.indent();
558: newline = false;
559: printer.p("/* value = ").p(v.value).p(':').p(v.list).p("; */");
560: }
561:
562: }
|