001: /*
002: * CSharpParserFile.java
003: *
004: * This work is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU General Public License as published
006: * by the Free Software Foundation; either version 2 of the License,
007: * or (at your option) any later version.
008: *
009: * This work is distributed in the hope that it will be useful, but
010: * WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017: * USA
018: *
019: * As a special exception, the copyright holders of this library give
020: * you permission to link this library with independent modules to
021: * produce an executable, regardless of the license terms of these
022: * independent modules, and to copy and distribute the resulting
023: * executable under terms of your choice, provided that you also meet,
024: * for each linked independent module, the terms and conditions of the
025: * license of that module. An independent module is a module which is
026: * not derived from or based on this library. If you modify this
027: * library, you may extend this exception to your version of the
028: * library, but you are not obligated to do so. If you do not wish to
029: * do so, delete this exception statement from your version.
030: *
031: * Copyright (c) 2003 Per Cederberg. All rights reserved.
032: */
033:
034: package net.percederberg.grammatica.output;
035:
036: import java.io.IOException;
037: import java.util.HashMap;
038:
039: import net.percederberg.grammatica.code.csharp.CSharpClass;
040: import net.percederberg.grammatica.code.csharp.CSharpComment;
041: import net.percederberg.grammatica.code.csharp.CSharpConstructor;
042: import net.percederberg.grammatica.code.csharp.CSharpEnumeration;
043: import net.percederberg.grammatica.code.csharp.CSharpFile;
044: import net.percederberg.grammatica.code.csharp.CSharpMethod;
045: import net.percederberg.grammatica.code.csharp.CSharpNamespace;
046: import net.percederberg.grammatica.code.csharp.CSharpUsing;
047: import net.percederberg.grammatica.parser.ProductionPattern;
048: import net.percederberg.grammatica.parser.ProductionPatternAlternative;
049: import net.percederberg.grammatica.parser.ProductionPatternElement;
050:
051: /**
052: * The C# parser file generator. This class encapsulates all the
053: * C# code necessary for creating a parser.
054: *
055: * @author Per Cederberg, <per at percederberg dot net>
056: * @version 1.0
057: */
058: class CSharpParserFile {
059:
060: /**
061: * The type comment.
062: */
063: private static final String TYPE_COMMENT = "<remarks>A token stream parser.</remarks>";
064:
065: /**
066: * The production enumeration comment.
067: */
068: private static final String ENUM_COMMENT = "<summary>An enumeration with the generated production node\n"
069: + "identity constants.</summary>";
070:
071: /**
072: * The first constructor comment.
073: */
074: private static final String CONSTRUCTOR1_COMMENT = "<summary>Creates a new parser.</summary>\n\n"
075: + "<param name='input'>the input stream to read from</param>\n\n"
076: + "<exception cref='ParserCreationException'>if the parser\n"
077: + "couldn't be initialized correctly</exception>";
078:
079: /**
080: * The second constructor comment.
081: */
082: private static final String CONSTRUCTOR2_COMMENT = "<summary>Creates a new parser.</summary>\n\n"
083: + "<param name='input'>the input stream to read from</param>\n\n"
084: + "<param name='analyzer'>the analyzer to parse with</param>\n\n"
085: + "<exception cref='ParserCreationException'>if the parser\n"
086: + "couldn't be initialized correctly</exception>";
087:
088: /**
089: * The init method comment.
090: */
091: private static final String INIT_METHOD_COMMENT = "<summary>Initializes the parser by creating all the production\n"
092: + "patterns.</summary>\n\n"
093: + "<exception cref='ParserCreationException'>if the parser\n"
094: + "couldn't be initialized correctly</exception>";
095:
096: /**
097: * The parser generator.
098: */
099: private CSharpParserGenerator gen;
100:
101: /**
102: * The tokenizer file generator.
103: */
104: private CSharpTokenizerFile tokenizer;
105:
106: /**
107: * The file to write.
108: */
109: private CSharpFile file;
110:
111: /**
112: * The class to write.
113: */
114: private CSharpClass cls;
115:
116: /**
117: * The syntetic contants enumeration.
118: */
119: private CSharpEnumeration enm;
120:
121: /**
122: * The class initializer method.
123: */
124: private CSharpMethod initMethod;
125:
126: /**
127: * A map with the production constants in this class. This map
128: * is indexed with the pattern id and contains the production
129: * constant name.
130: */
131: private HashMap constantNames = new HashMap();
132:
133: /**
134: * The first available constant id number.
135: */
136: private int constantId = 1;
137:
138: /**
139: * Creates a new parser file.
140: *
141: * @param gen the parser generator to use
142: * @param tokenizer the tokenizer file generator
143: */
144: public CSharpParserFile(CSharpParserGenerator gen,
145: CSharpTokenizerFile tokenizer) {
146:
147: String name = gen.getBaseName() + "Parser";
148: int modifiers;
149:
150: this .gen = gen;
151: this .tokenizer = tokenizer;
152: this .file = new CSharpFile(gen.getBaseDir(), name);
153: if (gen.getPublicAccess()) {
154: modifiers = CSharpClass.PUBLIC;
155: } else {
156: modifiers = CSharpClass.INTERNAL;
157: }
158: this .cls = new CSharpClass(modifiers, name,
159: "RecursiveDescentParser");
160: this .enm = new CSharpEnumeration(CSharpEnumeration.PRIVATE,
161: "SynteticPatterns");
162: this .initMethod = new CSharpMethod(CSharpMethod.PRIVATE,
163: "CreatePatterns", "", "void");
164: initializeCode();
165: }
166:
167: /**
168: * Initializes the source code objects.
169: */
170: private void initializeCode() {
171: CSharpConstructor constr;
172: String str;
173:
174: // Add using
175: file.addUsing(new CSharpUsing("System.IO"));
176: file
177: .addUsing(new CSharpUsing(
178: "PerCederberg.Grammatica.Parser"));
179:
180: // Add namespace
181: if (gen.getNamespace() == null) {
182: file.addClass(cls);
183: } else {
184: CSharpNamespace n = new CSharpNamespace(gen.getNamespace());
185: n.addClass(cls);
186: file.addNamespace(n);
187: }
188:
189: // Add file comment
190: str = file.toString() + "\n\n" + gen.getFileComment();
191: file.addComment(new CSharpComment(CSharpComment.BLOCK, str));
192:
193: // Add type comment
194: cls.addComment(new CSharpComment(TYPE_COMMENT));
195:
196: // Add enumeration
197: cls.addEnumeration(enm);
198: enm.addComment(new CSharpComment(ENUM_COMMENT));
199:
200: // Add constructor
201: constr = new CSharpConstructor("TextReader input");
202: cls.addConstructor(constr);
203: constr.addComment(new CSharpComment(CONSTRUCTOR1_COMMENT));
204: constr.addInitializer("base("
205: + tokenizer.getConstructorCall("input") + ")");
206: constr.addCode("CreatePatterns();");
207:
208: // Add constructor
209: constr = new CSharpConstructor("TextReader input, "
210: + "Analyzer analyzer");
211: cls.addConstructor(constr);
212: constr.addComment(new CSharpComment(CONSTRUCTOR2_COMMENT));
213: constr
214: .addInitializer("base("
215: + tokenizer.getConstructorCall("input")
216: + ", analyzer)");
217: constr.addCode("CreatePatterns();");
218:
219: // Add init method
220: cls.addMethod(initMethod);
221: initMethod.addComment(new CSharpComment(INIT_METHOD_COMMENT));
222: initMethod.addCode("ProductionPattern pattern;");
223: initMethod.addCode("ProductionPatternAlternative alt;");
224: }
225:
226: /**
227: * Adds a production constant definition to this file.
228: *
229: * @param pattern the production pattern
230: */
231: public void addProductionConstant(ProductionPattern pattern) {
232: String constant;
233:
234: if (pattern.isSyntetic()) {
235: constant = "SUBPRODUCTION_" + constantId;
236: enm
237: .addConstant(constant, String
238: .valueOf(constantId + 3000));
239: constantNames.put(new Integer(pattern.getId()), constant);
240: constantId++;
241: }
242: }
243:
244: /**
245: * Adds a production pattern definition to this file.
246: *
247: * @param pattern the production pattern
248: * @param constants the constants file generator
249: */
250: public void addProduction(ProductionPattern pattern,
251: CSharpConstantsFile constants) {
252: StringBuffer code;
253: String str;
254:
255: // Create new pattern
256: code = new StringBuffer();
257: code.append("pattern = new ProductionPattern((int) ");
258: code.append(getConstant(constants, pattern.getId()));
259: code.append(",\n");
260: code.append(" \"");
261: if (pattern.isSyntetic()) {
262: str = (String) constantNames.get(new Integer(pattern
263: .getId()));
264: code.append(gen.getCodeStyle().getMixedCase(str, true));
265: } else {
266: code.append(pattern.getName());
267: }
268: code.append("\");");
269: initMethod.addCode("");
270: initMethod.addCode(code.toString());
271:
272: // Set syntetic flag
273: if (pattern.isSyntetic()) {
274: initMethod.addCode("pattern.SetSyntetic(true);");
275: }
276:
277: // Create pattern rules
278: for (int i = 0; i < pattern.getAlternativeCount(); i++) {
279: addProductionAlternative(pattern.getAlternative(i),
280: constants);
281: }
282:
283: // Add pattern to parser
284: initMethod.addCode("AddPattern(pattern);");
285: }
286:
287: /**
288: * Adds a production pattern alternative definition to the init
289: * method.
290: *
291: * @param alt the production pattern alternative
292: * @param constants the constants file generator
293: */
294: private void addProductionAlternative(
295: ProductionPatternAlternative alt,
296: CSharpConstantsFile constants) {
297:
298: ProductionPatternElement elem;
299: StringBuffer code;
300:
301: initMethod.addCode("alt = new ProductionPatternAlternative();");
302: for (int i = 0; i < alt.getElementCount(); i++) {
303: elem = alt.getElement(i);
304: code = new StringBuffer();
305: code.append("alt.");
306: if (elem.isToken()) {
307: code.append("AddToken(");
308: } else {
309: code.append("AddProduction(");
310: }
311: code.append("(int) ");
312: code.append(getConstant(constants, elem.getId()));
313: code.append(", ");
314: code.append(elem.getMinCount());
315: code.append(", ");
316: if (elem.getMaxCount() == Integer.MAX_VALUE) {
317: code.append("-1");
318: } else {
319: code.append(elem.getMaxCount());
320: }
321: code.append(");");
322: initMethod.addCode(code.toString());
323: }
324: initMethod.addCode("pattern.AddAlternative(alt);");
325: }
326:
327: /**
328: * Returns the constant name for a specified pattern or token id.
329: *
330: * @param constants the constants file
331: * @param id the pattern id
332: *
333: * @return the constant name to use
334: */
335: private String getConstant(CSharpConstantsFile constants, int id) {
336: Integer value = new Integer(id);
337:
338: if (constantNames.containsKey(value)) {
339: return "SynteticPatterns." + constantNames.get(value);
340: } else {
341: return constants.getConstant(id);
342: }
343: }
344:
345: /**
346: * Writes the file source code.
347: *
348: * @throws IOException if the output file couldn't be created
349: * correctly
350: */
351: public void writeCode() throws IOException {
352: file.writeCode(gen.getCodeStyle());
353: }
354: }
|