0001: /*
0002: * xtc - The eXTensible Compiler
0003: * Copyright (C) 2005-2007 Robert Grimm
0004: *
0005: * This program is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU General Public License
0007: * version 2 as published by the Free Software Foundation.
0008: *
0009: * This program is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0012: * GNU General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU General Public License
0015: * along with this program; if not, write to the Free Software
0016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0017: * USA.
0018: */
0019: package xtc.parser;
0020:
0021: import java.io.File;
0022: import java.io.FileNotFoundException;
0023: import java.io.IOException;
0024: import java.io.Reader;
0025:
0026: import java.util.ArrayList;
0027: import java.util.Collections;
0028: import java.util.HashMap;
0029: import java.util.HashSet;
0030: import java.util.IdentityHashMap;
0031: import java.util.Iterator;
0032: import java.util.List;
0033: import java.util.Map;
0034: import java.util.Set;
0035:
0036: import xtc.Constants;
0037:
0038: import xtc.tree.Attribute;
0039: import xtc.tree.Visitor;
0040:
0041: import xtc.type.AST;
0042: import xtc.type.ErrorT;
0043: import xtc.type.Type;
0044:
0045: import xtc.util.Runtime;
0046: import xtc.util.Utilities;
0047:
0048: /**
0049: * Visitor to resolve grammar module dependencies.
0050: *
0051: * <p />Note that this visitor {@link TextTester marks} text-only
0052: * productions as such. It also {@link LeftRecurser detects} left
0053: * recursions and marks direct left recursions. Furthermore, it sets
0054: * a grammar's {@link Properties#GENERIC} and {@link
0055: * Properties#RECURSIVE} properties as appropriate.
0056: *
0057: * @author Robert Grimm
0058: * @version $Revision: 1.124 $
0059: */
0060: public class Resolver extends Visitor {
0061:
0062: /** The runtime. */
0063: protected final Runtime runtime;
0064:
0065: /** The analyzer utility. */
0066: protected final Analyzer analyzer;
0067:
0068: /** The type operations. */
0069: protected final AST ast;
0070:
0071: /** The current checking phase. */
0072: protected int phase;
0073:
0074: /** The identity hash map of erroneous nonterminals. */
0075: protected Map<NonTerminal, NonTerminal> badNTs;
0076:
0077: /** The flag for whether the current module has a stateful attribute. */
0078: protected boolean hasState;
0079:
0080: /** The flag for whether the current module is a mofunctor. */
0081: protected boolean isMofunctor;
0082:
0083: /** Flag for whether the current expression is a predicate. */
0084: protected boolean isPredicate;
0085:
0086: /** The set of sequence names for the current production. */
0087: protected Set<SequenceName> sequenceNames;
0088:
0089: /**
0090: * Create a new resolver.
0091: *
0092: * @param runtime The runtime.
0093: * @param analyzer The analyzer utility.
0094: * @param ast The type operations.
0095: */
0096: public Resolver(Runtime runtime, Analyzer analyzer, AST ast) {
0097: this .runtime = runtime;
0098: this .analyzer = analyzer;
0099: this .ast = ast;
0100: badNTs = new IdentityHashMap<NonTerminal, NonTerminal>();
0101: sequenceNames = new HashSet<SequenceName>();
0102: }
0103:
0104: /**
0105: * Print the specified module's signature.
0106: *
0107: * @param m The module.
0108: */
0109: protected void signature(Module m) {
0110: // Print the globally visible module name.
0111: System.out.print("module ");
0112: System.out.print(m.name.name);
0113:
0114: // If the module has been instantiated from another, print the
0115: // original invocation.
0116: if (m.name.hasProperty(Constants.ORIGINAL)
0117: || (null != m.parameters)) {
0118: System.out.print(" = ");
0119: ModuleName base = m.name.hasProperty(Constants.ORIGINAL) ? (ModuleName) m.name
0120: .getProperty(Constants.ORIGINAL)
0121: : m.name;
0122: System.out.print(base.name);
0123: if (null == m.parameters) {
0124: System.out.print("()");
0125: } else {
0126: System.out.print(m.parameters.toString());
0127: }
0128: }
0129:
0130: if (null != m.dependencies) {
0131: // Print the modification, if any.
0132: for (ModuleDependency dep : m.dependencies) {
0133: if (dep.isModification()) {
0134: System.out.println();
0135: System.out.print(" modifies ");
0136: System.out.print(dep.visibleName().name);
0137: break;
0138: }
0139: }
0140:
0141: // Print the imports, if any.
0142: boolean first = true;
0143: for (ModuleDependency dep : m.dependencies) {
0144: if (dep.isImport()) {
0145: if (first) {
0146: System.out.println();
0147: System.out.print(" imports ");
0148: first = false;
0149: } else {
0150: System.out.println(',');
0151: System.out.print(" ");
0152: }
0153: System.out.print(dep.visibleName().name);
0154: }
0155: }
0156: }
0157:
0158: // Done.
0159: System.out.println(';');
0160: }
0161:
0162: /**
0163: * Load the module with the specified name.
0164: *
0165: * @param name The name.
0166: * @return The corresponding grammar module.
0167: * @throws IllegalArgmentException
0168: * Signals that the file is too large.
0169: * @throws FileNotFoundException
0170: * Signals that the file does not exist.
0171: * @throws IOException
0172: * Signals an exceptional condition while accessing the file.
0173: * @throws ParseException
0174: * Signals a parse error.
0175: */
0176: protected Module load(String name) throws IOException,
0177: ParseException {
0178: File file = runtime.locate(Utilities.toPath(name,
0179: Constants.EXT_GRAMMAR));
0180:
0181: if (!file.exists()) {
0182: throw new FileNotFoundException(file + ": file not found");
0183: } else if (!file.isFile()) {
0184: throw new IllegalArgumentException(file + ": not a file");
0185: } else if (Integer.MAX_VALUE < file.length()) {
0186: throw new IllegalArgumentException(file
0187: + ": file too large");
0188: }
0189:
0190: Reader in = null;
0191: try {
0192: in = runtime.getReader(file);
0193: PParser parser = new PParser(in, file.toString(),
0194: (int) file.length());
0195: return (Module) parser.value(parser.pModule(0));
0196: } finally {
0197: if (null != in) {
0198: try {
0199: in.close();
0200: } catch (IOException x) {
0201: // Nothing to see here. Move on.
0202: }
0203: }
0204: }
0205: }
0206:
0207: /**
0208: * Rename the specified module. This method renames all module
0209: * names in the specified module, including the module's {@link
0210: * Module#name name}, the module's {@link Module#dependencies
0211: * dependencies}, and each production's {@link Production#qName
0212: * qualified name} (if it is not <code>null</code>).
0213: *
0214: * @param module The module.
0215: * @param renaming The renaming.
0216: */
0217: protected void rename(Module module, ModuleMap renaming) {
0218: // Process the name.
0219: module.name = module.name.rename(renaming);
0220:
0221: // Process the parameters.
0222: if (null != module.parameters) {
0223: module.parameters.rename(renaming);
0224: }
0225:
0226: // Process the dependencies.
0227: if (null != module.dependencies) {
0228: for (ModuleDependency dep : module.dependencies)
0229: dep.rename(renaming);
0230: }
0231:
0232: // Process the productions.
0233: Renamer renamer = new Renamer(runtime, analyzer, renaming);
0234: for (Production p : module.productions) {
0235: if (null != p.qName) {
0236: p.qName = p.qName.rename(renaming);
0237: }
0238: renamer.dispatch(p);
0239: }
0240: }
0241:
0242: /**
0243: * Strip the specified production's ordered choice.
0244: *
0245: * @see Analyzer#stripChoices
0246: *
0247: * @param p The production to strip.
0248: * @return The specified production.
0249: */
0250: protected Production strip(Production p) {
0251: if ((null != p) && (null != p.choice)) {
0252: p.choice = Analyzer.stripChoices(p.choice);
0253: }
0254: return p;
0255: }
0256:
0257: /**
0258: * Apply the specified alternative addition to the specified full
0259: * production.
0260: *
0261: * @param p1 The alternative addition.
0262: * @param p2 The full production.
0263: */
0264: protected void apply(AlternativeAddition p1, FullProduction p2) {
0265: final int length2 = p2.choice.alternatives.size();
0266: int index = -1;
0267: for (int i = 0; i < length2; i++) {
0268: if (p1.sequence.equals(p2.choice.alternatives.get(i).name)) {
0269: index = i;
0270: break;
0271: }
0272: }
0273:
0274: if (-1 == index) {
0275: StringBuilder buf = new StringBuilder();
0276: buf.append("unable to add new alternative");
0277: if (1 != p1.choice.alternatives.size()) {
0278: buf.append('s');
0279: }
0280: if (p1.isBefore) {
0281: buf.append(" before ");
0282: } else {
0283: buf.append(" after ");
0284: }
0285: buf.append("non-existent alternative '");
0286: buf.append(p1.sequence.name);
0287: buf.append("'");
0288: runtime.error(buf.toString(), p1.sequence);
0289:
0290: } else {
0291: if (!p1.isBefore) {
0292: index++;
0293: }
0294: p2.choice.alternatives
0295: .addAll(index, p1.choice.alternatives);
0296: }
0297: }
0298:
0299: /**
0300: * Apply the specified alternative removal to the specified full
0301: * production.
0302: *
0303: * @param p1 The alternative removal.
0304: * @param p2 The full production.
0305: */
0306: protected void apply(AlternativeRemoval p1, FullProduction p2) {
0307: for (SequenceName name : p1.sequences) {
0308: boolean removed = false;
0309:
0310: for (Iterator<Sequence> iter = p2.choice.alternatives
0311: .iterator(); iter.hasNext();) {
0312: if (name.equals(iter.next().name)) {
0313: iter.remove();
0314: removed = true;
0315: break;
0316: }
0317: }
0318:
0319: if (!removed) {
0320: runtime.error(
0321: "unable to remove non-existent alternative '"
0322: + name + "'", name);
0323: }
0324: }
0325: }
0326:
0327: /**
0328: * Apply the specified production override to the specified full
0329: * production.
0330: *
0331: * @param p1 The production override.
0332: * @param p2 The full production.
0333: */
0334: protected void apply(ProductionOverride p1, FullProduction p2) {
0335: // Override the attributes.
0336: if (null != p1.attributes) {
0337: p2.attributes = p1.attributes;
0338: }
0339:
0340: // Override the element.
0341: if (null != p1.choice) {
0342: if (p1.isComplete) {
0343: p2.choice = p1.choice;
0344:
0345: } else {
0346: for (Sequence s1 : p1.choice.alternatives) {
0347: if (null == s1.name) {
0348: runtime.error(
0349: "overriding sequence without name", s1);
0350:
0351: } else {
0352: final int length2 = p2.choice.alternatives
0353: .size();
0354: boolean changed = false;
0355: for (int i = 0; i < length2; i++) {
0356: Sequence s2 = p2.choice.alternatives.get(i);
0357: if (s1.name.equals(s2.name)) {
0358: p2.choice.alternatives.set(i, s1);
0359: changed = true;
0360: break;
0361: }
0362: }
0363:
0364: if (!changed) {
0365: runtime.error(
0366: "unable to override non-existent alternative '"
0367: + s1.name + "'", s1);
0368: }
0369: }
0370: }
0371: }
0372: }
0373: }
0374:
0375: /**
0376: * Load all dependent modules. This method loads the transitive
0377: * closure of all dependent modules for the specified top-level
0378: * module and returns the closure as a list, with the specified
0379: * module being the first list element. It checks that the
0380: * specified top-level module is not parameterized, that the
0381: * parameters of parameterized modules are well-formed, that
0382: * arguments match parameters when instantiating parameterized
0383: * modules, that different instantiations are consistent with each
0384: * other, that a module does not have more than one modifies clause,
0385: * and that module modifications do not result in circular
0386: * dependencies. This method also reports any file access and
0387: * parsing errors. Finally, this method fills in the auxiliary
0388: * modification field for all loaded modules.
0389: *
0390: * @param m The module.
0391: * @return The corresponding grammar or <code>null</code> in the
0392: * case of errors.
0393: */
0394: protected Grammar load(Module m) {
0395: // Make sure the top-level module is not parameterized.
0396: if ((null != m.parameters) && (0 < m.parameters.size())) {
0397: runtime.error("parameterized top-level module '"
0398: + m.name.name + "'", m.name);
0399: return null;
0400: }
0401:
0402: // Print top-level module if so requested.
0403: PrettyPrinter pp = null;
0404: if (runtime.test("optionLoaded")) {
0405: pp = new PrettyPrinter(runtime.console(), ast, true);
0406: pp.dispatch(m);
0407: pp.flush();
0408: }
0409:
0410: // Prepare for loading all dependent modules. The modules list
0411: // contains all correctly loaded modules. The module map contains
0412: // a mapping between visible module names (as strings) and
0413: // modules. The loaded map contains a mapping between visible
0414: // module names and the corresponding module dependencies (even if
0415: // the module contained errors). The conflicts map is treated as
0416: // a set that contains conflicting module dependencies that were
0417: // already reported to the user.
0418: List<Module> modules = new ArrayList<Module>();
0419: Map<String, Module> moduleMap = new HashMap<String, Module>();
0420: Map<ModuleName, ModuleDependency> loaded = new HashMap<ModuleName, ModuleDependency>();
0421: Map<ModuleDependency, Boolean> conflicts = new IdentityHashMap<ModuleDependency, Boolean>();
0422: modules.add(m);
0423: moduleMap.put(m.name.name, m);
0424: loaded.put(m.name, new ModuleImport(m.name));
0425:
0426: // Process the top-level module's dependencies and fill in the
0427: // auxiliary modification field.
0428: List<ModuleDependency> workList = new ArrayList<ModuleDependency>();
0429: if (null != m.dependencies) {
0430: for (ModuleDependency dep : m.dependencies) {
0431: if (dep.isModification()) {
0432: // Only process one modification per module and do not allow
0433: // self-mutilation.
0434: boolean process = true;
0435: if (null != m.modification) {
0436: runtime.error("duplicate modifies declaration",
0437: dep);
0438: process = false;
0439: }
0440: if (dep.visibleName().equals(m.name)) {
0441: runtime.error("module '" + m.name
0442: + "' modifies itself", dep);
0443: process = false;
0444: }
0445: if (process) {
0446: m.modification = (ModuleModification) dep;
0447: workList.add(dep);
0448: }
0449:
0450: } else {
0451: workList.add(dep);
0452: }
0453: }
0454: }
0455:
0456: // Print the signature if so requested.
0457: if (runtime.test("optionDependencies")) {
0458: signature(m);
0459: }
0460:
0461: // Do the actual loading.
0462: while (!workList.isEmpty()) {
0463: ModuleDependency dep = workList.remove(0);
0464:
0465: // Make sure that every module is processed only once.
0466: if (loaded.containsKey(dep.visibleName())) {
0467: ModuleDependency dep2 = loaded.get(dep.visibleName());
0468:
0469: // Since modules live in a global name space, all dependencies
0470: // need to be consistent with each other. In particular, two
0471: // dependency declarations for the a module with the same name
0472: // either need to have exactly the same structure or the later
0473: // declaration must be a straight-forward declaration without
0474: // any arguments or target name.
0475: if (!dep.isConsistentWith(dep2)) {
0476: if (!conflicts.containsKey(dep2)) {
0477: conflicts.put(dep2, Boolean.TRUE);
0478: runtime.error(
0479: "inconsistent instantiation of module '"
0480: + dep2.module + dep2.arguments
0481: + "' as '" + dep2.visibleName()
0482: + "'", dep2);
0483: }
0484: runtime.error(
0485: "inconsistent instantiation of module '"
0486: + dep.module + dep.arguments
0487: + "' as '" + dep.visibleName()
0488: + "'", dep);
0489: }
0490:
0491: continue;
0492: }
0493:
0494: // Be verbose if so desired.
0495: if (runtime.test("optionVerbose")) {
0496: String path = Utilities.toPath(dep.module.name,
0497: Constants.EXT_GRAMMAR);
0498: File file = null;
0499: try {
0500: file = runtime.locate(path);
0501: } catch (Exception x) {
0502: // Ignore.
0503: }
0504: if (null == file) {
0505: System.err.println("[Loading module " + dep.module
0506: + "]");
0507: } else {
0508: System.err.println("[Loading module " + dep.module
0509: + " from " + file + "]");
0510: }
0511: }
0512:
0513: // Load module.
0514: Module m2 = null;
0515:
0516: try {
0517: m2 = load(dep.module.name);
0518: } catch (IllegalArgumentException x) {
0519: runtime.error(x.getMessage(), dep);
0520:
0521: } catch (FileNotFoundException x) {
0522: runtime.error(x.getMessage(), dep);
0523:
0524: } catch (IOException x) {
0525: if (null == x.getMessage()) {
0526: runtime
0527: .error(
0528: "I/O error while accessing corresponding file",
0529: dep);
0530: } else {
0531: runtime.error(x.getMessage(), dep);
0532: }
0533:
0534: } catch (ParseException x) {
0535: System.err.print(x.getMessage());
0536: runtime.error();
0537: }
0538:
0539: // Remember that we touched this module, even if it didn't load
0540: // as there is no need to generate the same error message more
0541: // than once.
0542: loaded.put(dep.visibleName(), dep);
0543:
0544: // All the other work requires that we actually loaded the
0545: // module.
0546: if (null == m2)
0547: continue;
0548:
0549: // Print the module if so requested.
0550: if (runtime.test("optionLoaded")) {
0551: pp.dispatch(m2);
0552: pp.flush();
0553: }
0554:
0555: // Track errors in this module.
0556: boolean moduleError = false;
0557:
0558: // Make sure the module name matches the desired module name.
0559: if (!dep.module.equals(m2.name)) {
0560: String path = Utilities.toPath(dep.module.name,
0561: Constants.EXT_GRAMMAR);
0562: File file = null;
0563: try {
0564: file = runtime.locate(path);
0565: } catch (Exception x) {
0566: // Ignore.
0567: }
0568: if (null == file) {
0569: runtime.error("module name '" + m2.name
0570: + "' inconsistent with file name", m2.name);
0571: moduleError = true;
0572: } else {
0573: runtime.error("module name '" + m2.name
0574: + "' inconsistent with file name " + file,
0575: m2.name);
0576: moduleError = true;
0577: }
0578: }
0579:
0580: // Check the module's parameters (if any).
0581: if (null != m2.parameters) {
0582: final int size = m2.parameters.size();
0583: for (int i = 0; i < size; i++) {
0584: ModuleName name = m2.parameters.get(i);
0585:
0586: if (m2.name.equals(name)) {
0587: runtime.error("module parameter '" + name
0588: + "' same as module name", name);
0589: moduleError = true;
0590: }
0591:
0592: for (int j = 0; j < i; j++) {
0593: if (name.equals(m2.parameters.get(j))) {
0594: runtime.error(
0595: "duplicate module parameter '"
0596: + name + "'", name);
0597: moduleError = true;
0598: break;
0599: }
0600: }
0601: }
0602: }
0603:
0604: // Check that argument and parameter numbers match.
0605: final int argumentNumber = dep.arguments.size();
0606: final int parameterNumber = (null != m2.parameters) ? m2.parameters
0607: .size()
0608: : 0;
0609: if (argumentNumber != parameterNumber) {
0610: StringBuilder buf = new StringBuilder();
0611: buf.append(argumentNumber);
0612: buf.append(" argument");
0613: if (1 != argumentNumber) {
0614: buf.append('s');
0615: }
0616: buf.append(" for module '");
0617: buf.append(m2.name.name);
0618: buf.append("' with ");
0619: buf.append(parameterNumber);
0620: buf.append(" parameter");
0621: if (1 != parameterNumber) {
0622: buf.append('s');
0623: }
0624: runtime.error(buf.toString(), dep);
0625: moduleError = true;
0626: }
0627:
0628: // Only continue processing this module if there were no errors.
0629: if (moduleError)
0630: continue;
0631:
0632: // Perform any module renaming.
0633: if ((0 != argumentNumber)
0634: || (!dep.module.equals(dep.visibleName()))) {
0635: if (runtime.test("optionVerbose")) {
0636: StringBuilder buf = new StringBuilder();
0637:
0638: buf.append("[Instantiating module ");
0639: buf.append(m2.name);
0640: buf.append('(');
0641: for (int i = 0; i < argumentNumber; i++) {
0642: buf.append(dep.arguments.get(i).name);
0643: buf.append('/');
0644: buf.append(m2.parameters.get(i).name);
0645: if (i + 1 < argumentNumber) {
0646: buf.append(", ");
0647: }
0648: }
0649: buf.append(") as ");
0650: buf.append(dep.visibleName().name);
0651: buf.append(']');
0652: System.err.println(buf.toString());
0653: }
0654:
0655: // Construct a module map for renaming.
0656: final ModuleMap renaming;
0657: if (0 != argumentNumber) {
0658: renaming = new ModuleMap(m2.parameters,
0659: dep.arguments);
0660: } else {
0661: renaming = new ModuleMap();
0662: }
0663:
0664: // If the module's declared name and the visible name differ,
0665: // add the corresponding mapping to the module map.
0666: if (!dep.module.equals(dep.visibleName())) {
0667: renaming.put(dep.module, dep.visibleName());
0668: }
0669:
0670: // Rename the module.
0671: rename(m2, renaming);
0672:
0673: // The parameters are nulled out below to enable the printing
0674: // of module dependencies.
0675: }
0676:
0677: // Add the module to the list of modules and put the module in
0678: // the module map.
0679: modules.add(m2);
0680: moduleMap.put(m2.name.name, m2);
0681:
0682: // Process any further dependencies and fill in the module's
0683: // auxiliary modification field.
0684: if (null != m2.dependencies) {
0685: for (ModuleDependency dep2 : m2.dependencies) {
0686: if (dep2.isModification()) {
0687: // Only process one modification per module and do not
0688: // allow self-mutilation.
0689: boolean process = true;
0690: if (null != m2.modification) {
0691: runtime.error(
0692: "duplicate modifies declaration",
0693: dep2);
0694: process = false;
0695: }
0696: if (dep2.visibleName().equals(m2.name)) {
0697: runtime.error("module '" + m2.name
0698: + "' modifies itself", dep2);
0699: process = false;
0700: }
0701: if (process) {
0702: m2.modification = (ModuleModification) dep2;
0703: workList.add(dep2);
0704: }
0705:
0706: } else {
0707: workList.add(dep2);
0708: }
0709: }
0710: }
0711:
0712: // Print the signature, if so desired.
0713: if (runtime.test("optionDependencies")) {
0714: signature(m2);
0715: }
0716: if (null != m2.parameters) {
0717: m2.setProperty(Constants.ARGUMENTS, m2.parameters);
0718: m2.parameters = null;
0719: }
0720: }
0721:
0722: // Now, check for circular module modifications. The checked set
0723: // tracks the names of modules that have been checked. The
0724: // checking map contains a mapping from module names (as module
0725: // names) to booleans. False indicates that the module has been
0726: // checked once. True indicates that the module has been checked
0727: // twice, meaning it is part of a circular chain of dependencies.
0728: Map<ModuleName, Boolean> checking = new HashMap<ModuleName, Boolean>();
0729: Set<ModuleName> checked = new HashSet<ModuleName>();
0730: for (Module m2 : modules) {
0731: // If the module has been checked already, move on.
0732: if (checked.contains(m2.name))
0733: continue;
0734:
0735: // Reset the checking map.
0736: checking.clear();
0737:
0738: do {
0739: if (checking.containsKey(m2.name)) {
0740: boolean circular = checking.get(m2.name);
0741:
0742: if (circular) {
0743: // We are checking this module for the third time and can
0744: // therefore stop walking the dependencies.
0745: break;
0746:
0747: } else {
0748: // We are checking this module for the second time.
0749: checking.put(m2.name, Boolean.TRUE);
0750: }
0751:
0752: } else {
0753: // We are checking this module for the first time.
0754: checking.put(m2.name, Boolean.FALSE);
0755: }
0756:
0757: // If this module has a modifies clause, we check the
0758: // corresponding module next.
0759: if (null != m2.modification) {
0760: // Look up the corresponding module.
0761: m2 = moduleMap
0762: .get(m2.modification.visibleName().name);
0763:
0764: if (null == m2) {
0765: // This is an unresolved or erroneous dependency. We need
0766: // to stop here.
0767: break;
0768: }
0769:
0770: } else {
0771: // This module does not modify another.
0772: break;
0773: }
0774: } while (true);
0775:
0776: // Remember all visited modules and report errors for all
0777: // circular dependencies. We do this in order of the resolved
0778: // modules to print errors in a standardized order.
0779: for (Module m3 : modules) {
0780: if (checking.containsKey(m3.name)) {
0781: // Remember as visited.
0782: checked.add(m3.name);
0783:
0784: // Report error.
0785: if (checking.get(m3.name)) {
0786: runtime.error("circular modifies dependency",
0787: m3.modification);
0788: }
0789: }
0790: }
0791: }
0792:
0793: // Create the grammar.
0794: Grammar g = new Grammar(modules);
0795:
0796: // Print instantiated modules, if so requested.
0797: if (runtime.test("optionInstantiated")) {
0798: if (runtime.test("optionHtml")) {
0799: new HtmlPrinter(runtime, analyzer, ast, false)
0800: .dispatch(g);
0801: } else {
0802: pp = new PrettyPrinter(runtime.console(), ast, true);
0803: pp.dispatch(g);
0804: pp.flush();
0805: }
0806: }
0807:
0808: // We are done.
0809: return runtime.seenError() ? null : g;
0810: }
0811:
0812: /**
0813: * Perform basic checking for the specified grammar. This method
0814: * does all correctness checking besides checking the contents of
0815: * partial productions and checking for left-recursive definitions.
0816: * Note that this method initializes the analyzer with the specified
0817: * grammar.
0818: *
0819: * @param g The grammar.
0820: * @return The list of global attributes, if any.
0821: */
0822: protected List<Attribute> check(Grammar g) {
0823: // Initialize the analyzer.
0824: analyzer.register(this );
0825: analyzer.init(g);
0826: phase = 1;
0827:
0828: // Initialize the top-level module and the list of global
0829: // attributes.
0830: Module root = g.modules.get(0);
0831: List<Attribute> attributes = new ArrayList<Attribute>();
0832:
0833: // Make sure that any stateful attributes agree in their class
0834: // names. Additionally, collect any stateful, set, or flag
0835: // attributes in the global list of attributes.
0836: String stateName = null;
0837: boolean stateError = false;
0838: for (Module m2 : g.modules) {
0839: // Process stateful attribute.
0840: if (m2.hasAttribute(Constants.ATT_STATEFUL.getName())) {
0841: Attribute state = Attribute.get(Constants.ATT_STATEFUL
0842: .getName(), m2.attributes);
0843: Object value = state.getValue();
0844: if ((null != value) && (value instanceof String)
0845: && (!((String) value).startsWith("\""))) {
0846: if (null == stateName) {
0847: attributes.add(state);
0848: stateName = (String) state.getValue();
0849: }
0850: if (!stateName.equals(state.getValue())) {
0851: stateError = true;
0852: }
0853: }
0854: }
0855:
0856: // Process set and flag attributes.
0857: if (m2.hasAttribute(Constants.NAME_STRING_SET)
0858: || m2.hasAttribute(Constants.NAME_FLAG)) {
0859: for (Attribute att : m2.attributes) {
0860: String name = att.getName();
0861: if ((Constants.NAME_STRING_SET.equals(name) || Constants.NAME_FLAG
0862: .equals(name))
0863: && (!attributes.contains(att))) {
0864: attributes.add(att);
0865: }
0866: }
0867: }
0868: }
0869:
0870: // Now, do the actual well-formedness checking.
0871: Set<ModuleName> visited = new HashSet<ModuleName>();
0872: boolean hasTopLevel = false;
0873: for (Module m2 : g.modules) {
0874: analyzer.process(m2);
0875: hasState = false;
0876: isMofunctor = (null != m2.modification);
0877:
0878: if (null != m2.attributes) {
0879: final int length = m2.attributes.size();
0880: for (int i = 0; i < length; i++) {
0881: Attribute att = m2.attributes.get(i);
0882: String name = att.getName();
0883: Object value = att.getValue();
0884:
0885: if ((!Constants.ATT_WITH_LOCATION.equals(att))
0886: && (!Constants.ATT_CONSTANT.equals(att))
0887: && (!Constants.ATT_RAW_TYPES.equals(att))
0888: && (!Constants.ATT_VERBOSE.equals(att))
0889: && (!Constants.ATT_NO_WARNINGS.equals(att))
0890: && (!Constants.ATT_IGNORING_CASE
0891: .equals(att))
0892: && (!Constants.ATT_STATEFUL.getName()
0893: .equals(name))
0894: && (!Constants.NAME_PARSER.equals(name))
0895: && (!Constants.NAME_MAIN.equals(name))
0896: && (!Constants.NAME_PRINTER.equals(name))
0897: && (!Constants.NAME_VISIBILITY.equals(name))
0898: && (!Constants.NAME_STRING_SET.equals(name))
0899: && (!Constants.NAME_FLAG.equals(name))
0900: && (!Constants.NAME_FACTORY.equals(name))
0901: && (!Constants.ATT_FLATTEN.equals(att))
0902: && (!Constants.ATT_GENERIC_AS_VOID
0903: .equals(att))
0904: && (!Constants.ATT_PARSE_TREE.equals(att))
0905: && (!Constants.ATT_PROFILE.equals(att))
0906: && (!Constants.ATT_DUMP.equals(att))) {
0907: runtime.error(
0908: "unrecognized grammar-wide attribute '"
0909: + att + "'", att);
0910:
0911: } else {
0912: for (int j = 0; j < i; j++) {
0913: Attribute att2 = m2.attributes.get(j);
0914: if (name.equals(Constants.NAME_STRING_SET)
0915: || name.equals(Constants.NAME_FLAG)) {
0916: if (att.equals(att2)) {
0917: runtime.error(
0918: "duplicate attribute '"
0919: + att + "'", att);
0920: break;
0921: } else if ((null != value)
0922: && value
0923: .equals(att2.getValue())) {
0924: runtime.error(
0925: "duplicate field name '"
0926: + att + "'", att);
0927: }
0928:
0929: } else if (name.equals(att2.getName())) {
0930: runtime.error("duplicate attribute '"
0931: + name + "'", att);
0932: break;
0933: }
0934: }
0935: }
0936:
0937: if (Constants.ATT_STATEFUL.getName().equals(name)) {
0938: if (null == value) {
0939: runtime
0940: .error(
0941: "stateful attribute without class name",
0942: att);
0943: } else if (!(value instanceof String)) {
0944: runtime
0945: .error(
0946: "stateful attribute with invalid value",
0947: att);
0948: } else if (((String) value).startsWith("\"")) {
0949: runtime
0950: .error(
0951: "stateful attribute with invalid value",
0952: att);
0953: } else if (stateError) {
0954: runtime
0955: .error(
0956: "inconsistent state class across modules",
0957: att);
0958: }
0959: hasState = true;
0960:
0961: } else if (Constants.NAME_PARSER.equals(name)) {
0962: if (null == value) {
0963: runtime
0964: .error(
0965: "parser attribute without class name",
0966: att);
0967: } else if (!(value instanceof String)) {
0968: runtime
0969: .error(
0970: "parser attribute with invalid value",
0971: att);
0972: } else if (((String) value).startsWith("\"")) {
0973: runtime
0974: .error(
0975: "parser attribute with invalid value",
0976: att);
0977: }
0978:
0979: } else if (Constants.NAME_MAIN.equals(name)) {
0980: if (runtime.test("optionLGPL")) {
0981: runtime
0982: .error(
0983: "main attribute incompatible with LGPL",
0984: att);
0985: } else if (null == value) {
0986: runtime
0987: .error(
0988: "main attribute without nonterminal value",
0989: att);
0990: } else if (!(value instanceof String)) {
0991: runtime
0992: .error(
0993: "main attribute with invalid value",
0994: att);
0995: } else if (((String) value).startsWith("\"")) {
0996: runtime
0997: .error(
0998: "main attribute with invalid value",
0999: att);
1000: } else {
1001: NonTerminal nt = new NonTerminal(
1002: (String) value);
1003: Production p = null;
1004: boolean err = false;
1005:
1006: try {
1007: p = analyzer.lookup(nt);
1008: } catch (IllegalArgumentException x) {
1009: runtime.error(
1010: "main attribute with ambiguous nonterminal '"
1011: + nt + "'", att);
1012: err = true;
1013: }
1014:
1015: if (!err) {
1016: if (null == p) {
1017: runtime.error(
1018: "main attribute with undefined nonterminal '"
1019: + nt + "'", att);
1020: } else if (!analyzer.isDefined(p, m2)) {
1021: runtime.error(
1022: "main attribute with another module's "
1023: + "nonterminal '"
1024: + nt + "'", att);
1025: } else if (!p
1026: .hasAttribute(Constants.ATT_PUBLIC)) {
1027: runtime.error(
1028: "main attribute with non-public nonterminal '"
1029: + nt + "'", att);
1030: }
1031: }
1032: }
1033:
1034: } else if (Constants.NAME_PRINTER.equals(name)) {
1035: if (runtime.test("optionLGPL")) {
1036: runtime
1037: .error(
1038: "printer attribute incompatible with LGPL",
1039: att);
1040: } else if (null == value) {
1041: runtime
1042: .error(
1043: "printer attribute without class name",
1044: att);
1045: } else if (!(value instanceof String)) {
1046: runtime
1047: .error(
1048: "printer attribute with invalid value",
1049: att);
1050: } else if (((String) value).startsWith("\"")) {
1051: runtime
1052: .error(
1053: "printer attribute with invalid value",
1054: att);
1055: }
1056: if (!m2.hasAttribute(Constants.NAME_MAIN)) {
1057: runtime
1058: .error(
1059: "printer attribute without main attribute",
1060: att);
1061: }
1062:
1063: } else if (Constants.NAME_VISIBILITY.equals(name)) {
1064: if (null == value) {
1065: runtime
1066: .error(
1067: "visibility attribute without value",
1068: att);
1069: } else if ((!Constants.ATT_PUBLIC.getValue()
1070: .equals(value))
1071: && (!Constants.ATT_PACKAGE_PRIVATE
1072: .getValue().equals(value))) {
1073: runtime
1074: .error(
1075: "visibility attribute with invalid value",
1076: att);
1077: }
1078:
1079: } else if (Constants.NAME_STRING_SET.equals(name)) {
1080: if (null == value) {
1081: runtime
1082: .error(
1083: "string set attribute without set value",
1084: att);
1085: } else if (!(value instanceof String)) {
1086: runtime
1087: .error(
1088: "string set attribute with invalid value",
1089: att);
1090: } else if (((String) value).startsWith("\"")) {
1091: runtime
1092: .error(
1093: "string set attribute with invalid value",
1094: att);
1095: }
1096:
1097: } else if (Constants.NAME_FLAG.equals(name)) {
1098: if (null == value) {
1099: runtime
1100: .error(
1101: "flag attribute without flag value",
1102: att);
1103: } else if (!(value instanceof String)) {
1104: runtime
1105: .error(
1106: "flag attribute with invalid value",
1107: att);
1108: } else if (((String) value).startsWith("\"")) {
1109: runtime
1110: .error(
1111: "flag attribute with invalid value",
1112: att);
1113: }
1114:
1115: } else if (Constants.NAME_FACTORY.equals(name)) {
1116: if (null == value) {
1117: runtime
1118: .error(
1119: "factory attribute without class name",
1120: att);
1121: } else if (!(value instanceof String)) {
1122: runtime
1123: .error(
1124: "factory attribute with invalid value",
1125: att);
1126: } else if (((String) value).startsWith("\"")) {
1127: runtime
1128: .error(
1129: "factory attribute with invalud value",
1130: att);
1131: }
1132:
1133: } else if (Constants.ATT_GENERIC_AS_VOID
1134: .equals(att)) {
1135: if (m2.hasAttribute(Constants.ATT_PARSE_TREE)) {
1136: runtime
1137: .error(
1138: "genericAsVoid attribute incompatible with "
1139: + "withParseTree attribute",
1140: att);
1141: }
1142:
1143: } else if (Constants.ATT_DUMP.equals(att)) {
1144: if (runtime.test("optionLGPL")) {
1145: runtime
1146: .error(
1147: "dump attribute incompatible with LGPL",
1148: att);
1149: }
1150:
1151: } else if (Constants.ATT_RAW_TYPES.equals(att)) {
1152: runtime
1153: .warning(
1154: "the rawTypes attribute has been deprecated",
1155: att);
1156: runtime
1157: .errConsole()
1158: .loc(att)
1159: .pln(
1160: ": warning: and will be removed in a future release")
1161: .flush();
1162: }
1163: }
1164: }
1165:
1166: // If this module does not have a stateful attribute, check the
1167: // the current module's direct imports as well as any modified
1168: // modules and their direct imports.
1169: if (!hasState) {
1170: hasState = analyzer.hasAttribute(m2,
1171: Constants.ATT_STATEFUL.getName(), visited);
1172: visited.clear();
1173: }
1174:
1175: // Check the productions.
1176: for (Production p : m2.productions) {
1177: // Initialize the per-production state.
1178: sequenceNames.clear();
1179:
1180: // Process the production.
1181: analyzer.process(p);
1182:
1183: // Check for duplicate definitions.
1184: try {
1185: if ((!p.isPartial())
1186: && (p != analyzer.lookup(p.name))) {
1187: runtime.error(
1188: "duplicate definition for nonterminal '"
1189: + p.name + "'", p);
1190: }
1191: } catch (IllegalArgumentException x) {
1192: runtime.error(
1193: "duplicate definition for nonterminal '"
1194: + p.name + "'", p);
1195: }
1196:
1197: // Process top-level productions. Only public productions
1198: // from the top-level module are recognized as top-level for
1199: // the entire grammar.
1200: if (p.hasAttribute(Constants.ATT_PUBLIC)) {
1201: if (analyzer.isDefined(p, root)) {
1202: hasTopLevel = true;
1203: } else {
1204: p.attributes.remove(Constants.ATT_PUBLIC);
1205: }
1206: }
1207: }
1208: }
1209:
1210: // Check for top-level nonterminals.
1211: if (!hasTopLevel) {
1212: runtime.error("no public nonterminal",
1213: g.modules.get(0).productions.get(0));
1214: }
1215:
1216: // Done.
1217: return attributes;
1218: }
1219:
1220: /**
1221: * Apply any module modifications. This method applies all module
1222: * modifications appearing in the specified grammar. It also
1223: * removes unused modules from the grammar. This method assumes
1224: * that all module dependencies have been successfully resolved,
1225: * i.e., the specified grammar contains all module dependencies.
1226: * This method also assumes that each module modification's {@link
1227: * Module#modification modification} field has been correctly
1228: * initialized.
1229: *
1230: * @param g The grammar.
1231: */
1232: protected void applyModifications(Grammar g) {
1233: // Initialize the analyzer.
1234: analyzer.register(this );
1235: analyzer.init(g);
1236: phase = 2;
1237:
1238: // Calculate the transitive closure of imported and modified
1239: // modules.
1240: Module root = g.modules.get(0);
1241: Set<ModuleName> imports = new HashSet<ModuleName>();
1242: Map<ModuleName, Boolean> modified = new HashMap<ModuleName, Boolean>();
1243: imports.add(root.name);
1244: analyzer.trace(root, imports, modified);
1245:
1246: // Remove any modules that are not reachable from the top-level
1247: // module (i.e., modules that have been instantiated but never
1248: // imported or modified).
1249: for (Iterator<Module> iter = g.modules.iterator(); iter
1250: .hasNext();) {
1251: Module m = iter.next();
1252: if ((!imports.contains(m.name))
1253: && (!modified.containsKey(m.name))) {
1254: if (runtime.test("optionVerbose")) {
1255: System.err.println("[Unloading unused module "
1256: + m.name + "]");
1257: }
1258: analyzer.remove(m);
1259: iter.remove();
1260: }
1261: }
1262:
1263: // If there are any modifications, apply them.
1264: if (0 != modified.size()) {
1265: // Set up the list of module modifications (in dependency order)
1266: // and the base module they are applied to. Also track the set
1267: // of modules with errors. Furthermore, track the set of
1268: // modules checked in phase 2. Finally, create the maps for the
1269: // target module's productions and for both the target and
1270: // source modules' productions.
1271: List<Module> mofunctors = new ArrayList<Module>();
1272: Module base;
1273: Set<Module> erroneous = new HashSet<Module>();
1274: Set<Module> checked = new HashSet<Module>();
1275: Map<NonTerminal, Production> targetProductions = new HashMap<NonTerminal, Production>();
1276: Map<NonTerminal, Production> productions = new HashMap<NonTerminal, Production>();
1277:
1278: while (true) {
1279: // Find a module modification to apply.
1280: mofunctors.clear();
1281: base = null;
1282: for (Module m : g.modules) {
1283: if ((null != m.modification)
1284: && (!erroneous.contains(m))) {
1285: // We found a module modification without errors. Follow
1286: // the the chain of dependencies until we reach the base
1287: // module.
1288: do {
1289: mofunctors.add(m);
1290: m = analyzer.lookup(m.modification
1291: .visibleName());
1292: } while (null != m.modification);
1293: base = m;
1294:
1295: if (erroneous.contains(base)) {
1296: // We have seen an error before.
1297: erroneous.addAll(mofunctors);
1298: base = null;
1299: }
1300:
1301: // We are done looking for a module modification.
1302: break;
1303: }
1304: }
1305:
1306: if (null == base) {
1307: // Nothing left to do, either because all module
1308: // modifications have been applied or there have been
1309: // errors.
1310: break;
1311: }
1312:
1313: // Set up the target module.
1314: Module target = base;
1315:
1316: // Iterate over the module modifications and apply them to the
1317: // target.
1318: for (int i = mofunctors.size() - 1; i >= 0; i--) {
1319: Module source = mofunctors.get(i);
1320:
1321: if (runtime.test("optionVerbose")) {
1322: System.err.println("[Applying module "
1323: + source.name + " to module "
1324: + target.name + "]");
1325: }
1326:
1327: // If the target module is modified in more than one way or
1328: // both modified and imported, then we need to modify a
1329: // copy.
1330: if (modified.get(target.name)
1331: || (modified.containsKey(target.name) && imports
1332: .contains(target.name))) {
1333: if (runtime.test("optionVerbose")) {
1334: System.err
1335: .println("[Copying modified module "
1336: + target.name + "]");
1337: }
1338: target = analyzer.copy(target);
1339:
1340: } else {
1341: // Remove the module from the analyzer's state, since we
1342: // will update that state after applying the modification.
1343: // Also, remove the module from the grammar, as it
1344: // replaces the source module.
1345: if (runtime.test("optionVerbose")) {
1346: System.err
1347: .println("[Removing modified module "
1348: + target.name + "]");
1349: }
1350: analyzer.remove(target);
1351: g.remove(target);
1352: }
1353:
1354: // Since the source module is effectively replaced by the
1355: // target module, we remove it from the analyzer state and
1356: // replace it in the grammar. We need to preserve the
1357: // position in the grammar as the top-level module may be a
1358: // module modification.
1359: if (runtime.test("optionVerbose")) {
1360: System.err
1361: .println("[Removing modifying module "
1362: + source.name + "]");
1363: }
1364: analyzer.remove(source);
1365: g.replace(source, target);
1366:
1367: // Rename the target module.
1368: rename(target, new ModuleMap(target.name,
1369: source.name));
1370:
1371: // Patch the target module's documentation comment.
1372: target.documentation = source.documentation;
1373:
1374: // Explicitly replace the target module's name with the
1375: // source module's to preserve the original name, if any.
1376: target.name = source.name;
1377:
1378: // Similarly, patch the arguments property.
1379: target.removeProperty(Constants.ARGUMENTS);
1380: if (source.hasProperty(Constants.ARGUMENTS)) {
1381: target.setProperty(Constants.ARGUMENTS, source
1382: .getProperty(Constants.ARGUMENTS));
1383: }
1384:
1385: // Add the source module's dependencies, with exception of
1386: // the modification, to the target module's dependencies.
1387: // Note that we don't need to check whether the source
1388: // module has dependencies, as it must have at least one
1389: // modification dependency.
1390: Iterator<ModuleDependency> iter2 = source.dependencies
1391: .iterator();
1392: while (iter2.hasNext()) {
1393: if (iter2.next().isModification()) {
1394: iter2.remove();
1395: // We continue iterating, just in case that there are
1396: // several modifications.
1397: }
1398: }
1399:
1400: if (null == target.dependencies) {
1401: target.dependencies = source.dependencies;
1402: } else {
1403: target.dependencies.addAll(source.dependencies);
1404: }
1405:
1406: // Add the source module's header, body, and footer actions
1407: // to the target module, if they exist.
1408: if (null != source.header) {
1409: if (null == target.header) {
1410: target.header = source.header;
1411: } else {
1412: target.header.add(source.header);
1413: }
1414: }
1415:
1416: if (null != source.body) {
1417: if (null == target.body) {
1418: target.body = source.body;
1419: } else {
1420: target.body.add(source.body);
1421: }
1422: }
1423:
1424: if (null != source.footer) {
1425: if (null == target.footer) {
1426: target.footer = source.footer;
1427: } else {
1428: target.footer.add(source.footer);
1429: }
1430: }
1431:
1432: // Replace the target module's options with the source
1433: // module's options. However, if the target module has a
1434: // stateful, set, or flag option, while the source module
1435: // does not have that same option, add the corresponding
1436: // option to the list of options first.
1437: if (null != target.attributes) {
1438: // Process stateful attribute.
1439: if (target.hasAttribute(Constants.ATT_STATEFUL
1440: .getName())) {
1441: if (null == source.attributes) {
1442: source.attributes = new ArrayList<Attribute>();
1443: }
1444: if (!source
1445: .hasAttribute(Constants.ATT_STATEFUL
1446: .getName())) {
1447: source.attributes.add(Attribute.get(
1448: Constants.ATT_STATEFUL
1449: .getName(),
1450: target.attributes));
1451: }
1452: }
1453:
1454: // Process set and flag attributes.
1455: if (target
1456: .hasAttribute(Constants.NAME_STRING_SET)
1457: || target
1458: .hasAttribute(Constants.NAME_FLAG)) {
1459: if (null == source.attributes) {
1460: source.attributes = new ArrayList<Attribute>();
1461: }
1462: for (Attribute att : target.attributes) {
1463: String name = att.getName();
1464: if ((Constants.NAME_STRING_SET
1465: .equals(name) || Constants.NAME_FLAG
1466: .equals(name))
1467: && (!source.attributes
1468: .contains(att))) {
1469: source.attributes.add(att);
1470: }
1471: }
1472: }
1473: }
1474: target.attributes = source.attributes;
1475:
1476: // Build a map of the target module's productions and a map
1477: // of both the target and source modules' productions.
1478: // While building the maps, also strip each production.
1479: targetProductions.clear();
1480: productions.clear();
1481: for (Production p : target.productions) {
1482: p = strip(p);
1483: if (p.isFull()
1484: && (!productions.containsKey(p.name))) {
1485: targetProductions.put(p.name, p);
1486: productions.put(p.name, p);
1487: }
1488: }
1489: for (Production p : source.productions) {
1490: p = strip(p);
1491: if (p.isFull()
1492: && (!productions.containsKey(p.name))) {
1493: productions.put(p.name, p);
1494: }
1495: }
1496:
1497: // Process the source module's productions.
1498: int errorCount = runtime.errorCount();
1499: for (Production p1 : source.productions) {
1500: FullProduction p2 = (FullProduction) productions
1501: .get(p1.name);
1502:
1503: if (p1.isFull()) {
1504: // If the source module's production is a duplicate
1505: // definition, that error has already been reported.
1506: // Otherwise, we simply add it to the target module.
1507: if (!targetProductions.containsKey(p1.name)) {
1508: target.productions.add(p1);
1509: targetProductions.put(p1.name, p1);
1510: }
1511:
1512: } else if (null != p2) {
1513: if (p1.isAddition()) {
1514: apply((AlternativeAddition) p1, p2);
1515:
1516: } else if (p1.isRemoval()) {
1517: apply((AlternativeRemoval) p1, p2);
1518:
1519: } else if (p1.isOverride()) {
1520: apply((ProductionOverride) p1, p2);
1521:
1522: } else {
1523: throw new AssertionError(
1524: "Unrecognized production " + p1);
1525: }
1526: }
1527: }
1528:
1529: // Add the target module into the analyzer's state. Also,
1530: // mark the target module as checked in phase 2.
1531: if (runtime.test("optionVerbose")) {
1532: System.err.println("[Adding resulting module "
1533: + target.name + "]");
1534: }
1535: analyzer.add(target);
1536: checked.add(target);
1537:
1538: // Re-check the target module for duplicate sequence names
1539: // and for ambiguous or undefined nonterminals.
1540: analyzer.process(target);
1541: for (Production p : target.productions) {
1542: sequenceNames.clear();
1543: analyzer.process(p);
1544: }
1545:
1546: // If we have seen any errors, we mark the target module and
1547: // any remaining module modifications as erroneous.
1548: if (runtime.errorCount() != errorCount) {
1549: erroneous.add(target);
1550: List<Module> l = mofunctors.subList(0, i);
1551: erroneous.addAll(l);
1552: checked.addAll(l);
1553: }
1554: }
1555:
1556: // Recalculate the transitive closure of imported and modified
1557: // modules. We have to re-initialize the root, since that
1558: // module may have changed.
1559: root = g.modules.get(0);
1560: imports.clear();
1561: modified.clear();
1562: imports.add(root.name);
1563: analyzer.trace(root, imports, modified);
1564:
1565: // We do not need to remove unreachable modules, because the
1566: // initial removal has already removed all of them.
1567: }
1568:
1569: // Check all modules that have not been checked in phase 2 in
1570: // order to detect amgibuous nonterminals.
1571: phase = 3;
1572: for (Module m : g.modules) {
1573: if (!checked.contains(m)) {
1574: analyzer.process(m);
1575: for (Production p : m.productions) {
1576: analyzer.process(p);
1577: }
1578: }
1579: }
1580: }
1581:
1582: // Print the resulting modules if requested.
1583: if (runtime.test("optionApplied")) {
1584: if (runtime.test("optionHtml")) {
1585: new HtmlPrinter(runtime, analyzer, ast, false)
1586: .dispatch(g);
1587: } else {
1588: PrettyPrinter pp = new PrettyPrinter(runtime.console(),
1589: ast, true);
1590: pp.dispatch(g);
1591: pp.flush();
1592: }
1593: }
1594:
1595: // Done.
1596: }
1597:
1598: /**
1599: * Intern the grammar's types. This method assumes that all module
1600: * modifications have been applied.
1601: *
1602: * @param g The grammar.
1603: */
1604: protected void internTypes(Grammar g) {
1605: // Determine the grammar's features.
1606: boolean hasNode = false;
1607: boolean hasToken = false;
1608: boolean hasFormatting = false;
1609: boolean hasAction = false;
1610:
1611: // Check for parse trees.
1612: if (g.modules.get(0).hasAttribute(Constants.ATT_PARSE_TREE)) {
1613: hasToken = true;
1614: hasFormatting = true;
1615: }
1616:
1617: // Check for generic productions, including generic productions
1618: // that are directly left-recursive.
1619: for (Module m : g.modules) {
1620: for (Production p : m.productions) {
1621: // Check for generic productions.
1622: if (ast.isGenericNode(p.dType)) {
1623: hasNode = true;
1624:
1625: // Check for direct left recursions in generic productions.
1626: for (Sequence s : p.choice.alternatives) {
1627: final Element first = s.isEmpty() ? null
1628: : Analyzer.stripAndUnbind(s.get(0));
1629:
1630: if (p.name.equals(first)
1631: || p.qName.equals(first)) {
1632: hasAction = true;
1633: }
1634: }
1635: }
1636: }
1637: }
1638:
1639: // Only annotate the grammar if the productions are not going to
1640: // be voided out.
1641: if (!g.modules.get(0).hasAttribute(
1642: Constants.ATT_GENERIC_AS_VOID)) {
1643: if (hasNode) {
1644: g.modules.get(0).setProperty(Properties.GENERIC,
1645: Boolean.TRUE);
1646: }
1647: if (hasAction) {
1648: g.modules.get(0).setProperty(Properties.RECURSIVE,
1649: Boolean.TRUE);
1650: }
1651: }
1652:
1653: // Initialize the grammar's type map.
1654: ast.initialize(hasNode, hasToken, hasFormatting, hasAction);
1655:
1656: // Intern the types.
1657: for (Module m : g.modules) {
1658: for (Production p : m.productions) {
1659: Type t;
1660:
1661: try {
1662: t = ast.intern(p.dType);
1663: } catch (IllegalArgumentException x) {
1664: runtime.error(x.getMessage() + " for production '"
1665: + p.name + "'", p);
1666: t = ErrorT.TYPE;
1667: }
1668:
1669: p.type = t;
1670: }
1671: }
1672:
1673: // Check variant productions.
1674: for (Module m : g.modules) {
1675: for (Production p : m.productions) {
1676: if (p.hasAttribute(Constants.ATT_VARIANT)) {
1677: if (AST.isToken(p.type)) {
1678: runtime.error(
1679: "variant production for token type", p);
1680: } else if (!AST.isNode(p.type)) {
1681: runtime.error(
1682: "variant production without node type",
1683: p);
1684: }
1685: }
1686: }
1687: }
1688: }
1689:
1690: /**
1691: * Check for left-recursive productions. This method assumes that
1692: * all module modifications have been applied.
1693: *
1694: * @param g The grammar.
1695: */
1696: protected void checkRecursions(Grammar g) {
1697: new TextTester(runtime, analyzer).dispatch(g);
1698: LeftRecurser recurser = new LeftRecurser(runtime, analyzer);
1699: recurser.dispatch(g);
1700: Set<NonTerminal> recursive = recurser.recursive();
1701:
1702: for (Module m : g.modules) {
1703: for (Production p : m.productions) {
1704: if (recursive.contains(p.qName)) {
1705: // Report the production as malformed.
1706: runtime.error(
1707: "left-recursive definition for nonterminal '"
1708: + p.name + "'", p);
1709: }
1710: }
1711: }
1712: }
1713:
1714: /**
1715: * Check repetitions for elements that accept the empty input. This
1716: * method assumes that all module modifications have been applied.
1717: *
1718: * @param g The grammar.
1719: */
1720: protected void checkRepetitions(Grammar g) {
1721: analyzer.register(checkRepetitionsVisitor);
1722: analyzer.init(g);
1723:
1724: for (Module m : g.modules) {
1725: analyzer.process(m);
1726: for (Production p : m.productions) {
1727: if (p.isFull()) {
1728: analyzer.process(p);
1729: }
1730: }
1731: }
1732: }
1733:
1734: /** The visitor for checking repetitions. */
1735: @SuppressWarnings("unused")
1736: private final Visitor checkRepetitionsVisitor = new Visitor() {
1737: public void visit(Repetition r) {
1738: dispatch(r.element);
1739: if (analyzer.matchesEmpty(r.element)) {
1740: runtime
1741: .error("repeated element matches empty input",
1742: r);
1743: }
1744: }
1745:
1746: public void visit(Option o) {
1747: dispatch(o.element);
1748: if (analyzer.matchesEmpty(o.element)
1749: && (!analyzer.grammar().modules.get(0)
1750: .hasAttribute(Constants.ATT_NO_WARNINGS))
1751: && (!analyzer.current().hasAttribute(
1752: Constants.ATT_NO_WARNINGS))) {
1753: runtime.warning(
1754: "optional element already matches empty input",
1755: o);
1756: }
1757: }
1758:
1759: public void visit(Production p) {
1760: dispatch(p.choice);
1761: }
1762:
1763: public void visit(OrderedChoice c) {
1764: for (Sequence alt : c.alternatives)
1765: dispatch(alt);
1766: }
1767:
1768: public void visit(Sequence s) {
1769: for (Element e : s.elements)
1770: dispatch(e);
1771: }
1772:
1773: public void visit(UnaryOperator op) {
1774: dispatch(op.element);
1775: }
1776:
1777: public void visit(Element e) {
1778: // Nothing to do.
1779: }
1780: };
1781:
1782: /**
1783: * Combine the modules in the specified grammar. This method
1784: * combines the modules in the grammar by modifying the grammar's
1785: * top-level module and then returns that module.
1786: *
1787: * @param g The grammar.
1788: * @param attributes The list of global attributes.
1789: * @return The grammar as a single module.
1790: */
1791: protected Module combine(Grammar g, List<Attribute> attributes) {
1792: // Get the top-level module.
1793: Module m = g.modules.get(0);
1794:
1795: // Null out the dependencies.
1796: m.dependencies = null;
1797: m.modification = null;
1798:
1799: // Make sure that the single module has all global attributes.
1800: if (0 < attributes.size()) {
1801: if (null == m.attributes) {
1802: m.attributes = attributes;
1803:
1804: } else {
1805: for (Attribute att : attributes) {
1806: if (!m.attributes.contains(att)) {
1807: m.attributes.add(att);
1808: }
1809: }
1810: }
1811: }
1812:
1813: // Collect headers, bodies, and footers. We need to make sure
1814: // that we do not add the same header, body, or footer more than
1815: // once, since, in the presence of parameterized modules and
1816: // module modifications, the same source module can be
1817: // instantiated several times.
1818: List<Action> headers = new ArrayList<Action>();
1819: List<Action> bodies = new ArrayList<Action>();
1820: List<Action> footers = new ArrayList<Action>();
1821: for (Module m2 : g.modules) {
1822: if ((null != m2.header) && (!headers.contains(m2.header))) {
1823: headers.add(m2.header);
1824: }
1825:
1826: if ((null != m2.body) && (!bodies.contains(m2.body))) {
1827: bodies.add(m2.body);
1828: }
1829:
1830: if ((null != m2.footer) && (!footers.contains(m2.footer))) {
1831: footers.add(m2.footer);
1832: }
1833: }
1834:
1835: // Combine all unique headers, bodies, and footers into a single
1836: // header, body, and footer, respectively.
1837: m.header = null;
1838: for (Action a : headers) {
1839: if (null == m.header) {
1840: m.header = a;
1841: } else {
1842: m.header.add(a);
1843: }
1844: }
1845:
1846: m.body = null;
1847: for (Action a : bodies) {
1848: if (null == m.body) {
1849: m.body = a;
1850: } else {
1851: m.body.add(a);
1852: }
1853: }
1854:
1855: m.footer = null;
1856: for (Action a : footers) {
1857: if (null == m.footer) {
1858: m.footer = a;
1859: } else {
1860: m.footer.add(a);
1861: }
1862: }
1863:
1864: // Now add in all productions.
1865: for (Module m2 : g.modules) {
1866: if (m != m2) {
1867: m.productions.addAll(m2.productions);
1868: }
1869: }
1870:
1871: return m;
1872: }
1873:
1874: /**
1875: * Visit the specified module.
1876: *
1877: * @param m The module to resolve.
1878: * @return A single, self-contained grammar module or
1879: * <code>null</code> on an error condition.
1880: */
1881: public Object visit(Module m) {
1882: // Reset the resolver state.
1883: badNTs.clear();
1884:
1885: // ----------------------------------------------------------------------
1886: // Load all dependent modules.
1887: // ----------------------------------------------------------------------
1888:
1889: Grammar g = load(m);
1890:
1891: // Only continue if there were no errors.
1892: if (runtime.seenError() || runtime.test("optionLoaded")
1893: || runtime.test("optionDependencies")
1894: || runtime.test("optionInstantiated")) {
1895: return null;
1896: }
1897:
1898: // ----------------------------------------------------------------------
1899: // Check as much as possible.
1900: // ----------------------------------------------------------------------
1901:
1902: List<Attribute> attributes = check(g);
1903:
1904: // ----------------------------------------------------------------------
1905: // Apply any module modifications.
1906: // ----------------------------------------------------------------------
1907:
1908: applyModifications(g);
1909: if (runtime.test("optionApplied"))
1910: return null;
1911:
1912: // ----------------------------------------------------------------------
1913: // Intern types.
1914: // ----------------------------------------------------------------------
1915:
1916: internTypes(g);
1917:
1918: // ----------------------------------------------------------------------
1919: // Check left-recursions and repeated elements
1920: // ----------------------------------------------------------------------
1921:
1922: checkRecursions(g);
1923: checkRepetitions(g);
1924: new ReachabilityChecker(runtime, analyzer).dispatch(g);
1925:
1926: // Only continue if there were no errors.
1927: if (runtime.seenError())
1928: return null;
1929:
1930: // ----------------------------------------------------------------------
1931: // Rename nonterminals for consistency.
1932: // ----------------------------------------------------------------------
1933:
1934: analyzer.uniquify();
1935:
1936: // ----------------------------------------------------------------------
1937: // Combine all modules into a single module.
1938: // ----------------------------------------------------------------------
1939:
1940: return combine(g, attributes);
1941: }
1942:
1943: /** Analyze the specified production. */
1944: public void visit(Production p) {
1945: if (1 == phase) {
1946: if (Constants.ATT_PACKAGE_PRIVATE.getValue()
1947: .equals(p.dType)
1948: || Constants.ATT_INLINE.getName().equals(p.dType)) {
1949: runtime.error("attribute '" + p.dType
1950: + "' as type for production '" + p.name + "'",
1951: p);
1952:
1953: } else if (Constants.ATT_WITH_LOCATION.getName().equals(
1954: p.dType)
1955: || Constants.ATT_CONSTANT.getName().equals(p.dType)
1956: || Constants.ATT_NO_INLINE.getName()
1957: .equals(p.dType)
1958: || Constants.ATT_MEMOIZED.getName().equals(p.dType)
1959: || Constants.ATT_VERBOSE.getName().equals(p.dType)
1960: || Constants.ATT_VARIANT.getName().equals(p.dType)
1961: || Constants.ATT_IGNORING_CASE.getName().equals(
1962: p.dType)
1963: || Constants.ATT_STATEFUL.getName().equals(p.dType)
1964: || Constants.ATT_RESETTING.getName()
1965: .equals(p.dType)) {
1966: if (!p.isPartial()
1967: && !analyzer.grammar().modules
1968: .get(0)
1969: .hasAttribute(Constants.ATT_NO_WARNINGS)
1970: && !p.hasAttribute(Constants.ATT_NO_WARNINGS)) {
1971: runtime.warning("attribute '" + p.dType
1972: + "' as type for production " + p.name
1973: + "'", p);
1974: }
1975: }
1976:
1977: if (p.isPartial()) {
1978: if (!isMofunctor) {
1979: runtime.error("production modification '" + p.name
1980: + "' without modifies declaration", p);
1981:
1982: } else {
1983: // Make sure the corresponding full production is defined
1984: // and the types match.
1985: Production p2 = null;
1986: try {
1987: p2 = analyzer.lookup(p.name);
1988: } catch (IllegalArgumentException x) {
1989: // Nothing to do.
1990: }
1991:
1992: if ((null == p2)
1993: || (!analyzer.isDefined(p2, analyzer
1994: .currentModule()))) {
1995: runtime.error("production modification '"
1996: + p.name + "' without full production",
1997: p);
1998:
1999: } else if (!p.dType.equals(p2.dType)) {
2000: runtime
2001: .error(
2002: "type '"
2003: + p.dType
2004: + "' of production modification '"
2005: + p.name
2006: + "' does not match full production's type",
2007: p);
2008: }
2009: }
2010: }
2011:
2012: if (null != p.attributes) {
2013: final int length = p.attributes.size();
2014: for (int i = 0; i < length; i++) {
2015: Attribute att = p.attributes.get(i);
2016:
2017: if ((!Constants.ATT_PUBLIC.equals(att))
2018: && (!Constants.ATT_PROTECTED.equals(att))
2019: && (!Constants.ATT_PRIVATE.equals(att))
2020: && (!Constants.ATT_TRANSIENT.equals(att))
2021: && (!Constants.ATT_INLINE.equals(att))
2022: && (!Constants.ATT_NO_INLINE.equals(att))
2023: && (!Constants.ATT_MEMOIZED.equals(att))
2024: && (!Constants.ATT_WITH_LOCATION
2025: .equals(att))
2026: && (!Constants.ATT_CONSTANT.equals(att))
2027: && (!Constants.ATT_VARIANT.equals(att))
2028: && (!Constants.ATT_VERBOSE.equals(att))
2029: && (!Constants.ATT_NO_WARNINGS.equals(att))
2030: && (!Constants.ATT_IGNORING_CASE
2031: .equals(att))
2032: && (!Constants.ATT_STATEFUL.equals(att))
2033: && (!Constants.ATT_RESETTING.equals(att))) {
2034: runtime.error(
2035: "unrecognized per-production attribute '"
2036: + att + "'", att);
2037:
2038: } else if ((!hasState)
2039: && Constants.ATT_STATEFUL.equals(att)) {
2040: runtime.error(
2041: "stateful attribute without grammar-wide stateful "
2042: + "attribute", att);
2043:
2044: } else if ((!hasState)
2045: && Constants.ATT_RESETTING.equals(att)) {
2046: runtime.error(
2047: "resetting attribute without grammar-wide stateful "
2048: + "attribute", att);
2049:
2050: } else {
2051: for (int j = 0; j < i; j++) {
2052: if (att.equals(p.attributes.get(j))) {
2053: runtime.error("duplicate attribute '"
2054: + att.getName() + "'", att);
2055: break;
2056: }
2057: }
2058: }
2059: }
2060:
2061: if (p.hasAttribute(Constants.ATT_MEMOIZED)) {
2062: if (p.hasAttribute(Constants.ATT_TRANSIENT)) {
2063: runtime
2064: .error(
2065: "memozied attribute contradicts transient attribute",
2066: Attribute.get(
2067: Constants.ATT_MEMOIZED
2068: .getName(),
2069: p.attributes));
2070: }
2071: if (p.hasAttribute(Constants.ATT_INLINE)) {
2072: runtime
2073: .error(
2074: "memoized attribute contradicts inline attribute",
2075: Attribute.get(
2076: Constants.ATT_MEMOIZED
2077: .getName(),
2078: p.attributes));
2079: }
2080: }
2081: if (p.hasAttribute(Constants.ATT_TRANSIENT)
2082: && p.hasAttribute(Constants.ATT_INLINE)) {
2083: runtime
2084: .error(
2085: "inline attribute subsumes transient attribute",
2086: Attribute.get(Constants.ATT_INLINE
2087: .getName(), p.attributes));
2088: }
2089: if (p.hasAttribute(Constants.ATT_INLINE)
2090: && p.hasAttribute(Constants.ATT_NO_INLINE)) {
2091: runtime
2092: .error(
2093: "inline attribute contradicts noinline attribute",
2094: Attribute.get(
2095: Constants.ATT_NO_INLINE
2096: .getName(),
2097: p.attributes));
2098: }
2099: }
2100: }
2101:
2102: dispatch(p.choice);
2103: }
2104:
2105: /** Analyze the specified ordered choice. */
2106: public void visit(OrderedChoice c) {
2107: for (Sequence alt : c.alternatives)
2108: dispatch(alt);
2109: }
2110:
2111: /** Analyze the specified repetition. */
2112: public void visit(Repetition r) {
2113: if (1 == phase) {
2114: if (Analyzer.strip(r.element) instanceof Action) {
2115: runtime.error("repeated action", r);
2116: }
2117: }
2118: dispatch(r.element);
2119: }
2120:
2121: /** Analyze the specified option. */
2122: public void visit(Option o) {
2123: if (1 == phase) {
2124: if (Analyzer.strip(o.element) instanceof Action) {
2125: runtime.error("optional action", o);
2126: }
2127: }
2128: dispatch(o.element);
2129: }
2130:
2131: /** Analyze the specified sequence. */
2132: public void visit(Sequence s) {
2133: if ((1 == phase) || (2 == phase)) {
2134: if (null != s.name) {
2135: if (sequenceNames.contains(s.name)) {
2136: runtime.error("duplicate sequence name '" + s.name
2137: + "'", s);
2138: } else {
2139: sequenceNames.add(s.name);
2140: }
2141: }
2142: }
2143:
2144: for (Element e : s.elements)
2145: dispatch(e);
2146: }
2147:
2148: /** Analyze the specified predicate. */
2149: public void visit(Predicate p) {
2150: if (1 == phase) {
2151: if (isPredicate) {
2152: runtime
2153: .error(
2154: "syntactic predicate within syntactic predicate",
2155: p);
2156: }
2157: }
2158:
2159: boolean pred = isPredicate;
2160: isPredicate = true;
2161: dispatch(p.element);
2162: isPredicate = pred;
2163: }
2164:
2165: /** Analyze the specified semantic predicate. */
2166: public void visit(SemanticPredicate p) {
2167: if (1 == phase) {
2168: if (!(p.element instanceof Action)) {
2169: runtime.error("malformed semantic predicate", p);
2170: } else {
2171: Action a = (Action) p.element;
2172: if ((null == a.code) || (0 >= a.code.size())) {
2173: runtime.error("empty test for semantic predicate",
2174: p);
2175: }
2176: }
2177: }
2178:
2179: dispatch(p.element);
2180: }
2181:
2182: /** Analyze the specified voided element. */
2183: public void visit(VoidedElement v) {
2184: if (1 == phase) {
2185: // We allow void:void:e and void:nt for void nonterminals, since
2186: // the simplifier removes the unnecessary voided elements.
2187: Element voided = Analyzer.strip(v.element);
2188:
2189: switch (voided.tag()) {
2190: case FOLLOWED_BY:
2191: case NOT_FOLLOWED_BY:
2192: case SEMANTIC_PREDICATE:
2193: runtime.error("voided predicate", v);
2194: break;
2195:
2196: case BINDING:
2197: runtime.error("voided binding", v);
2198: break;
2199:
2200: case ACTION:
2201: runtime.error("voided action", v);
2202: break;
2203:
2204: case PARSER_ACTION:
2205: runtime.error("voided parser action", v);
2206: break;
2207:
2208: case NODE_MARKER:
2209: runtime.error("voided node marker", v);
2210: break;
2211: }
2212: }
2213:
2214: dispatch(v.element);
2215: }
2216:
2217: /** Analyze the specified binding. */
2218: public void visit(Binding b) {
2219: if (1 == phase) {
2220: Element bound = Analyzer.strip(b.element);
2221:
2222: switch (bound.tag()) {
2223: case FOLLOWED_BY:
2224: case NOT_FOLLOWED_BY:
2225: case SEMANTIC_PREDICATE:
2226: runtime.error("binding for predicate", b);
2227: break;
2228:
2229: case VOIDED:
2230: runtime.error("binding for voided element", b);
2231: break;
2232:
2233: case BINDING:
2234: runtime.error("binding for binding", b);
2235: break;
2236:
2237: case NONTERMINAL: {
2238: NonTerminal nt = (NonTerminal) bound;
2239: Production p = null;
2240: try {
2241: p = analyzer.lookup(nt);
2242: } catch (IllegalArgumentException x) {
2243: // Nothing to do.
2244: }
2245: if ((null != p) && ast.isVoid(p.dType)) {
2246: runtime.error("binding for void nonterminal '" + nt
2247: + "'", b);
2248: }
2249: }
2250: break;
2251:
2252: case ACTION:
2253: runtime.error("binding for action", b);
2254: break;
2255:
2256: case PARSER_ACTION:
2257: runtime.error("binding for parser action", b);
2258: break;
2259:
2260: case NODE_MARKER:
2261: runtime.error("binding for node marker", b);
2262: break;
2263: }
2264: }
2265:
2266: dispatch(b.element);
2267: }
2268:
2269: /** Analyze the specified string match. */
2270: public void visit(StringMatch m) {
2271: if (1 == phase) {
2272: Element matched = Analyzer.strip(m.element);
2273:
2274: switch (matched.tag()) {
2275: case FOLLOWED_BY:
2276: case NOT_FOLLOWED_BY:
2277: case SEMANTIC_PREDICATE:
2278: runtime.error("string match on predicate", m);
2279: break;
2280:
2281: case VOIDED:
2282: runtime.error("string match on voided element", m);
2283: break;
2284:
2285: case BINDING:
2286: runtime.error("string match on binding "
2287: + "(try binding the match instead)", m);
2288: break;
2289:
2290: case STRING_MATCH:
2291: runtime
2292: .error("string match on another string match",
2293: m);
2294: break;
2295:
2296: case NONTERMINAL: {
2297: NonTerminal nt = (NonTerminal) matched;
2298: Production p = null;
2299: try {
2300: p = analyzer.lookup(nt);
2301: } catch (IllegalArgumentException x) {
2302: // Nothing to do.
2303: }
2304: if ((null != p) && ast.isVoid(p.dType)) {
2305: runtime.error("string match on void nonterminal '"
2306: + nt + "'", m);
2307: }
2308: }
2309: break;
2310:
2311: case ANY_CHAR:
2312: case CHAR_CLASS:
2313: case CHAR_LITERAL:
2314: case CHAR_SWITCH:
2315: case STRING_LITERAL:
2316: runtime.error("match for terminal", m);
2317: break;
2318:
2319: case ACTION:
2320: runtime.error("match for action", m);
2321: break;
2322:
2323: case PARSER_ACTION:
2324: runtime.error("match for parser action", m);
2325: break;
2326:
2327: case NODE_MARKER:
2328: runtime.error("match for node marker", m);
2329: break;
2330: }
2331: }
2332:
2333: dispatch(m.element);
2334: }
2335:
2336: /** Analyze the specified nonterminal. */
2337: public void visit(NonTerminal nt) {
2338: Production p = null;
2339:
2340: try {
2341: p = analyzer.lookup(nt);
2342: } catch (IllegalArgumentException x) {
2343: if (nt.hasProperty(Constants.ORIGINAL)) {
2344: if (!badNTs.containsKey(nt)) {
2345: runtime.error("ambiguous renamed nonterminal '"
2346: + nt + "'", nt);
2347: badNTs.put(nt, nt);
2348: }
2349: } else {
2350: if (!badNTs.containsKey(nt)) {
2351: runtime.error("ambiguous nonterminal '" + nt + "'",
2352: nt);
2353: badNTs.put(nt, nt);
2354: }
2355: }
2356: return;
2357: }
2358: if (null == p) {
2359: if (nt.hasProperty(Constants.ORIGINAL)) {
2360: if (!badNTs.containsKey(nt)) {
2361: runtime.error("undefined renamed nonterminal '"
2362: + nt + "'", nt);
2363: badNTs.put(nt, nt);
2364: }
2365: } else {
2366: if (!badNTs.containsKey(nt)) {
2367: runtime.error("undefined nonterminal '" + nt + "'",
2368: nt);
2369: badNTs.put(nt, nt);
2370: }
2371: }
2372: }
2373: }
2374:
2375: /** Analyze the specified terminal. */
2376: public void visit(Terminal t) {
2377: // Nothing to do.
2378: }
2379:
2380: /** Analyze the specified string literal. */
2381: public void visit(StringLiteral l) {
2382: if (1 == phase) {
2383: if (0 == l.text.length()) {
2384: runtime.error("empty string literal", l);
2385: }
2386: }
2387: }
2388:
2389: /** Analyze the specified character class. */
2390: public void visit(CharClass c) {
2391: if (1 != phase)
2392: return;
2393:
2394: final int length = c.ranges.size();
2395:
2396: if (0 >= length) {
2397: runtime.error("empty character class", c);
2398:
2399: } else {
2400: ArrayList<CharRange> list = new ArrayList<CharRange>(
2401: c.ranges);
2402: Collections.sort(list);
2403:
2404: for (int i = 0; i < length - 1; i++) {
2405: CharRange r1 = list.get(i);
2406: CharRange r2 = list.get(i + 1);
2407:
2408: if (r1.last >= r2.first) {
2409: boolean single1 = (r1.first == r1.last);
2410: boolean single2 = (r2.first == r2.last);
2411:
2412: if (single1) {
2413: if (single2) {
2414: runtime.error("duplicate character '"
2415: + Utilities.escape(r1.last,
2416: Utilities.FULL_ESCAPES)
2417: + "' in character class", c);
2418: } else {
2419: runtime.error("character '"
2420: + Utilities.escape(r1.last,
2421: Utilities.FULL_ESCAPES)
2422: + "' already contained in range "
2423: + Utilities.escape(r2.first,
2424: Utilities.FULL_ESCAPES)
2425: + "-"
2426: + Utilities.escape(r2.last,
2427: Utilities.FULL_ESCAPES), c);
2428: }
2429: } else {
2430: if (single2) {
2431: runtime.error("character '"
2432: + Utilities.escape(r2.first,
2433: Utilities.FULL_ESCAPES)
2434: + "' already contained in range "
2435: + Utilities.escape(r1.first,
2436: Utilities.FULL_ESCAPES)
2437: + "-"
2438: + Utilities.escape(r1.last,
2439: Utilities.FULL_ESCAPES), c);
2440: } else {
2441: runtime.error("ranges "
2442: + Utilities.escape(r1.first,
2443: Utilities.FULL_ESCAPES)
2444: + "-"
2445: + Utilities.escape(r1.last,
2446: Utilities.FULL_ESCAPES)
2447: + " and "
2448: + Utilities.escape(r2.first,
2449: Utilities.FULL_ESCAPES)
2450: + "-"
2451: + Utilities.escape(r2.last,
2452: Utilities.FULL_ESCAPES)
2453: + " overlap", c);
2454: }
2455: }
2456: }
2457: }
2458: }
2459: }
2460:
2461: /** Analyze the specified null literal. */
2462: public void visit(NullLiteral l) {
2463: // Nothing to do.
2464: }
2465:
2466: /** Analyze the specified node marker. */
2467: public void visit(NodeMarker m) {
2468: if (!ast.isGenericNode(analyzer.current().dType)) {
2469: runtime.error("node marker in non-generic production", m);
2470: } else if (isPredicate) {
2471: runtime.error("node marker in predicate", m);
2472: }
2473: }
2474:
2475: /** Analyze the specified action. */
2476: public void visit(Action a) {
2477: // Nothing to do.
2478: }
2479:
2480: /** Analyze the specified parser action. */
2481: public void visit(ParserAction pa) {
2482: if (1 == phase) {
2483: if (!(pa.element instanceof Action)) {
2484: runtime.error("malformed parser action", pa);
2485: }
2486: if (isPredicate) {
2487: runtime.error(
2488: "parser action within syntactic predicate", pa);
2489: }
2490: }
2491:
2492: dispatch(pa.element);
2493: }
2494:
2495: /** Analyze the specified internal element. */
2496: public void visit(InternalElement e) {
2497: if (1 == phase) {
2498: runtime.error("internal element", (Element) e);
2499: }
2500: }
2501:
2502: }
|