001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2005-2007 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: * version 2 as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
017: * USA.
018: */
019: package xtc.parser;
020:
021: import xtc.tree.Location;
022: import xtc.tree.Visitor;
023:
024: import xtc.util.Runtime;
025:
026: import xtc.type.AST;
027:
028: /**
029: * Visitor to void ordered choices, repetitions, and options. This
030: * visitor voids ordered choices, repetitions, and options, if all
031: * expressions appearing in such an expression are either void
032: * nonterminals or explicitly voided. It requires that a grammar has
033: * been {@link Resolver resolved} into a single module, that text-only
034: * productions have been {@link TextTester marked} as such, and that
035: * all expressions have been {@link Simplifier simplified}.
036: *
037: * @author Robert Grimm
038: * @version $Revision: 1.15 $
039: */
040: public class ElementVoider extends Visitor {
041:
042: /** The runtime. */
043: protected final Runtime runtime;
044:
045: /** The analyzer utility. */
046: protected final Analyzer analyzer;
047:
048: /** The flag for the top-level element. */
049: protected boolean isTopLevel;
050:
051: /** The flag for the last element. */
052: protected boolean isLastElement;
053:
054: /** The flag for suppressing the insertion of voided elements. */
055: protected boolean suppressVoided;
056:
057: /** The flag for potentially having a value. */
058: protected boolean hasValue;
059:
060: /**
061: * Create a new element voider.
062: *
063: * @param runtime The runtime.
064: * @param analyzer The analyzer.
065: */
066: public ElementVoider(Runtime runtime, Analyzer analyzer) {
067: this .runtime = runtime;
068: this .analyzer = analyzer;
069: }
070:
071: /**
072: * Wrap the specified element in a voiding operator.
073: *
074: * @param el The element to wrap.
075: * @return The wrapped element.
076: */
077: protected Element wrap(Element el) {
078: Element result = new VoidedElement(el);
079: Location loc = el.getLocation();
080: result.setLocation(loc);
081:
082: if (runtime.test("optionVerbose")) {
083: System.err.println("[Voiding expression at " + loc.line
084: + ":" + loc.column + " in "
085: + analyzer.current().qName + "]");
086: }
087:
088: return result;
089: }
090:
091: /** Visit the specified self-contained module. */
092: public void visit(Module m) {
093: // Initialize the per-grammar state.
094: analyzer.register(this );
095: analyzer.init(m);
096:
097: // Process the productioins.
098: for (Production p : m.productions)
099: analyzer.process(p);
100: }
101:
102: /** Visit the specified full production. */
103: public void visit(FullProduction p) {
104: // Text-only and token-level productions are ignored.
105: if (p.getBooleanProperty(Properties.TEXT_ONLY)
106: || p.getBooleanProperty(Properties.TOKEN)) {
107: return;
108: }
109:
110: isTopLevel = true;
111: isLastElement = false;
112: suppressVoided = AST.isVoid(p.type);
113: hasValue = false;
114:
115: p.choice = (OrderedChoice) dispatch(p.choice);
116: }
117:
118: /** Visit the specified ordered choice. */
119: public Element visit(OrderedChoice c) {
120: boolean top = isTopLevel;
121: isTopLevel = false;
122: boolean last = isLastElement;
123: boolean value = hasValue;
124:
125: if ((!top) && (!last)) {
126: hasValue = false;
127: }
128:
129: final int size = c.alternatives.size();
130: for (int i = 0; i < size; i++) {
131: isLastElement = top || last;
132: c.alternatives.set(i, (Sequence) dispatch(c.alternatives
133: .get(i)));
134: }
135:
136: isLastElement = false;
137: Element result = c;
138: if ((!top) && (!last)) {
139: if (hasValue) {
140: hasValue = true;
141: } else {
142: hasValue = value;
143: if (!suppressVoided) {
144: result = wrap(result);
145: }
146: }
147: }
148:
149: return result;
150: }
151:
152: /** Visit the specified quantification. */
153: public Element visit(Quantification q) {
154: isTopLevel = false;
155: isLastElement = true;
156: boolean value = hasValue;
157: hasValue = false;
158:
159: q.element = (Element) dispatch(q.element);
160:
161: isLastElement = false;
162: Element result = q;
163: if (hasValue) {
164: hasValue = true;
165: } else {
166: hasValue = value;
167: if (!suppressVoided) {
168: result = wrap(result);
169: }
170: }
171:
172: return result;
173: }
174:
175: /** Visit the specified sequence. */
176: public Element visit(Sequence s) {
177: isTopLevel = false;
178: boolean last = isLastElement;
179: final int size = s.size();
180:
181: for (int i = 0; i < size; i++) {
182: isLastElement = last && (i == size - 1);
183: s.elements.set(i, (Element) dispatch(s.get(i)));
184: }
185:
186: isLastElement = false;
187:
188: return s;
189: }
190:
191: /** Visit the specified predicate. */
192: public Element visit(Predicate p) {
193: isTopLevel = false;
194: isLastElement = true;
195: boolean value = hasValue;
196: hasValue = false;
197: boolean suppress = suppressVoided;
198: suppressVoided = true;
199:
200: p.element = (Element) dispatch(p.element);
201:
202: isLastElement = false;
203: hasValue = value;
204: suppressVoided = suppress;
205:
206: return p;
207: }
208:
209: /** Visit the specified voided element. */
210: public VoidedElement visit(VoidedElement v) {
211: isTopLevel = false;
212: isLastElement = true;
213: boolean value = hasValue;
214: hasValue = false;
215: boolean suppress = suppressVoided;
216: suppressVoided = true;
217:
218: v.element = (Element) dispatch(v.element);
219:
220: isLastElement = false;
221: hasValue = value;
222: suppressVoided = suppress;
223:
224: return v;
225: }
226:
227: /** Visit the specified binding. */
228: public Binding visit(Binding b) {
229: isTopLevel = false;
230: isLastElement = true;
231: hasValue = false;
232: boolean suppress = suppressVoided;
233: suppressVoided = false;
234:
235: b.element = (Element) dispatch(b.element);
236: if (b.element instanceof VoidedElement) {
237: runtime.error("binding for expression without value", b);
238: }
239:
240: isLastElement = false;
241: hasValue = true;
242: suppressVoided = suppress;
243:
244: return b;
245: }
246:
247: /** Visit the specified string match. */
248: public StringMatch visit(StringMatch m) {
249: isTopLevel = false;
250: isLastElement = true;
251: hasValue = false;
252: boolean suppress = suppressVoided;
253: suppressVoided = false;
254:
255: m.element = (Element) dispatch(m.element);
256: if (m.element instanceof VoidedElement) {
257: runtime.error("match for expression without value", m);
258: }
259:
260: isLastElement = false;
261: hasValue = true;
262: suppressVoided = suppress;
263:
264: return m;
265: }
266:
267: /** Visit the specified parser action. */
268: public ParserAction visit(ParserAction pa) {
269: isTopLevel = false;
270: isLastElement = false;
271: hasValue = true;
272:
273: return pa;
274: }
275:
276: /** Visit the specified action. */
277: public Action visit(Action a) {
278: isTopLevel = false;
279: isLastElement = false;
280:
281: if (a.setsValue()) {
282: hasValue = true;
283: }
284:
285: return a;
286: }
287:
288: /** Visit the specified nonterminal. */
289: public NonTerminal visit(NonTerminal nt) {
290: isTopLevel = false;
291: isLastElement = false;
292:
293: FullProduction p = analyzer.lookup(nt);
294: if (!AST.isVoid(p.type)) {
295: hasValue = true;
296: }
297:
298: return nt;
299: }
300:
301: /**
302: * Visit the specified element. This method provides the default
303: * implementation for terminals, parse tree nodes, null literals,
304: * node markers, and value elements.
305: */
306: public Element visit(Element e) {
307: isTopLevel = false;
308: isLastElement = false;
309: hasValue = true;
310: return e;
311: }
312:
313: }
|