/*******************************************************************************
* Copyright (c) 2008 SAP
* see https://research.qkal.sap.corp/mediawiki/index.php/CoMONET
*
* Date: $Date: 2009-10-14 14:21:35 +0200 (Mi, 14 Okt 2009) $
* @version $Revision: 8347 $
* @author: $Author: c5106462 $
*******************************************************************************/
package com.sap.furcas.parsergenerator.tcs.t2m.grammar;
import static com.sap.furcas.parsergenerator.util.StringConcatUtil.concat;
import java.util.ArrayList;
import java.util.List;
import com.sap.furcas.parsergenerator.GrammarGenerationException;
import com.sap.furcas.parsergenerator.tcs.t2m.grammar.rules.AbstractAntlr3Rule;
import com.sap.furcas.parsergenerator.util.VarStringBuffer;
import com.sap.furcas.runtime.common.interfaces.IModelElementProxy;
import com.sap.furcas.runtime.parser.ANTLR3LocationToken;
import com.sap.furcas.runtime.parser.IModelInjector;
import com.sap.furcas.runtime.parser.antlr3.DefaultANTLR3LocationTokenFactory;
import com.sap.furcas.runtime.parser.antlr3.ITokenFactory;
import com.sap.furcas.runtime.parser.impl.ObservableInjectingParser;
/**
* wraps functionality to write an ANTLR3 compatible grammar.
*
* @author C5107456
*/
public class ANTLR3GrammarWriter {
private Class<? extends ObservableInjectingParser> parserSuperClass = ObservableInjectingParser.class;
/** The Constant NEWLINES. */
private static final String NEWLINES = "\n\n";
/** The grammar options. */
private String grammarOptions = null;
private String targetPackage = "generated";
/** The lexer header. */
private String grammarName, lexerHeader = "@lexer::header {\n" + "package %package%;\n"
+ "import " + ANTLR3LocationToken.class.getCanonicalName() + ";\n" + "import "
+ ITokenFactory.class.getCanonicalName() + ";\n" + "import "
+ DefaultANTLR3LocationTokenFactory.class.getCanonicalName() + ";\n"
+ "import java.text.CharacterIterator;\n"
+ "import java.text.StringCharacterIterator;\n"
+ "import org.antlr.runtime.BitSet;\r\n"
+ "import org.antlr.runtime.IntStream;\r\n"
+ "import org.antlr.runtime.RecognitionException;"
// + "import org.antlr.runtime.Token"
+ "}";
/** The lexer members. */
private String lexerMembers = " public "
+ IModelInjector.class.getCanonicalName()
+ " ei = null;\n"
+ "public ITokenFactory<? extends ANTLR3LocationToken> tokenFactory;\n"
+ " /*************** Code generated in "
+ this.getClass()
+ " ********/\n"
+ " private void newline() {}\n"
+ "\n"
+ " public final Token emit() {\n"
+ " // override emit() to generate own token class\n"
+ " ANTLR3LocationToken ret = null;\n"
+ " ITokenFactory<? extends ANTLR3LocationToken> tokenFactory = getTokenFactory();\r\n"
+ " ret = tokenFactory.createToken(input, state.type, state.channel, state.tokenStartCharIndex, getCharIndex()-1);\r\n"
+ " ret.setLine(state.tokenStartLine);\r\n"
+ " String newtext = input.substring(state.tokenStartCharIndex,getCharIndex()-1);\r\n"
+ " ret.setText(newtext); // sets the text as exactly what has been read\r\n"
+ " ret.setCharPositionInLine(state.tokenStartCharPositionInLine);\r\n"
+ " ret.setEndLine(getLine());\r\n"
+ " ret.setEndColumn(getCharPositionInLine());\r\n"
+ " emit(ret);\r\n"
+ " return ret;\r\n"
+ " }\r\n"
+ " \r\n"
+ " private ITokenFactory<? extends ANTLR3LocationToken> getTokenFactory() {\r\n"
+ " if (tokenFactory == null) {\r\n"
+ " tokenFactory = new DefaultANTLR3LocationTokenFactory();\r\n"
+ " }\r\n"
+ " return tokenFactory;\r\n"
+ " }"
+ "\r\n"
+ " /**\r\n"
+ " * Removing starting/ending delimiters.\r\n"
+ " * @deprecated\n"
+ " */\r\n"
+ " public String unescapeString(String s, int delimLength) {\r\n"
+ " // get rid of the starting and ending delimiters (e.g., \'\\\'\', \'\"\')\r\n"
+ " if (s.charAt(0) == \'\\\'\' && s.charAt(s.length()-delimLength) == \'\\\'\' || s.charAt(0) == \'\\\"\' && s.charAt(s.length()-delimLength) == \'\\\"\') {\n"
+ " s = s.substring(delimLength, s.length()-(delimLength * 2 - 1));\r\n"
+ " } else if (s.length() >= 4 && s.charAt(0) == \'\\\\\' && s.charAt(s.length()-delimLength) == \'\\\"\' && s.charAt(1) == \'\\\"\' && s.charAt(s.length()-delimLength-1) == \'\\\\\') {\r\n"
+ " //also handle strings that are surrounded with an escaped string symbol \\\"value\\\"\r\n"
+ " delimLength += 1;\r\n"
+ " s = s.substring(delimLength, s.length()-(delimLength * 2 - 1));\r\n"
+ " }\n"
+ " return s;\r\n"
+ " }\n"
+ "\n"
+ " public void displayRecognitionError(String[] tokenNames, RecognitionException e) {\r\n"
+ " if (ei != null) {\r\n" + " ei.reportError(e);\r\n"
+ " } else {\r\n"
+ " super.displayRecognitionError(tokenNames, e);\r\n"
+ " }\r\n" + " \r\n" + " }\r\n"
+ " /*************** End of Code generated in " + this.getClass()
+ " ********/\r\n";
private String additionalLexerMembers = "";
/** The parser header. */
private final String parserHeader = "@header {\n" + "package %package%;\n" + "import "
+ ANTLR3LocationToken.class.getCanonicalName() + ";\n" + "import "
+ IModelElementProxy.class.getCanonicalName() + ";\n"
+ "import com.sap.furcas.runtime.parser.impl.PredicateSemantic;\n"
+ "import com.sap.furcas.runtime.parser.impl.SemanticDisambRuleData;\n"
+ "import com.sap.furcas.runtime.tcs.RuleNameFinder;\n"
+ "import java.text.CharacterIterator;\n"
+ "import java.text.StringCharacterIterator;\n" + "import %superclass%;\n"
+ "import org.antlr.runtime.Token;\n"
+ "import com.sap.furcas.runtime.referenceresolving.SyntaxRegistryFacade;\n"+ "}\n";
/** The parser members template. */
private static final String parserMembersTemplate = "@members {\n"
+ " private static final String syntaxUUID = \"%syntaxUUID%\";\r\n"
+ " public String getSyntaxUUID() {\r\n"
+ " return syntaxUUID;\r\n"
+ " }\r\n"
+ " public String unescapeString(String s) {\r\n"
+ " // get rid of the starting and ending delimiters (e.g., \'\\\'\', \'\"\')\r\n"
+ " int delimLength = 1; // for delimLength > 0, the following code needs to change\n"
+ " if (s.charAt(0) == \'\\\'\' && s.charAt(s.length()-delimLength) == \'\\\'\' || s.charAt(0) == \'\\\"\' && s.charAt(s.length()-delimLength) == \'\\\"\') {\n"
+ " s = s.substring(delimLength, s.length()-(delimLength * 2 - 1));\r\n"
+ " }\r\n"
+ " if(s.contains(\"\\\\\\\"\")) {\r\n"
+ " s = s.replaceAll(\"\\\\\\\\\\\"\", \"\\\"\");\r\n"
+ " }\r\n"
+ " return s;\r\n"
+ " }\n"
+ "\n"
+ "}\n";
/** The rules of the grammar. */
private final List<AbstractAntlr3Rule> rules = new ArrayList<AbstractAntlr3Rule>();
/** The fixed string for lexer rules. */
private String lexerString = "";
private String syntaxUUID;
/**
* Sets the grammar name.
*
* @param grammarName
* the new grammar name
*/
public void setGrammarName(final String grammarName) {
this.grammarName = grammarName;
}
/**
* Set the name of the java package that the parser class shall be later be placed in.
*/
public void setTargetPackage(String targetPackage) {
this.targetPackage = targetPackage;
}
/**
* Gets the grammar header.
*
* @return the grammar header
*/
public String getGrammarHeader() {
return concat("grammar ", grammarName, ';');
}
/**
* Sets the lexer header.
*
* @param lexerHeader
* the new lexer header
*/
public void setLexerHeader(final String lexerHeader) {
this.lexerHeader = concat("@lexer::header {", lexerHeader, '}');
}
/**
* Sets the lexer members.
*
* @param lexerMembers
* the new lexer members
*/
public void setLexerMembers(final String lexerMembers) {
this.lexerMembers = lexerMembers;
}
// /**
// * Sets the parser header.
// *
// * @param parserHeader the new parser header
// */
// public void setParserHeader(String parserHeader) {
// this.parserHeader = concat("@header {", parserHeader , '}');
// }
// public void setParserMembers(String parserMembers) {
// this.parserMembersTemplate = "@members {"+parserMembers +'}';
// }
/**
* Gets the output.
*
* @return the output
*
* @throws GrammarGenerationException
* the grammar generation exception
*/
public String getOutput() throws GrammarGenerationException {
if (grammarName == null) {
throw new GrammarGenerationException("Grammar name not set");
}
String parserMembers = parserMembersTemplate.replace("%Lexer%",
grammarName + "Lexer").replace("%Parser%", grammarName + "Parser");
parserMembers = parserMembers.replace("%syntaxUUID%", syntaxUUID.toString());
int newLinesLength = NEWLINES.length();
int grammarLength = getGrammarHeader().length() + lexerHeader.length()
+ lexerMembers.length() + 20 + additionalLexerMembers.length()
+ parserHeader.length() + parserMembers.length() + lexerString.length()
+ (newLinesLength * 5) + 10; // Slack for if I miscalculated
if (grammarOptions != null) {
grammarLength += grammarOptions.length() + 12;
}
for (AbstractAntlr3Rule rule : rules) {
grammarLength += rule.getLength() + newLinesLength;
}
VarStringBuffer buf = new VarStringBuffer(grammarLength);
buf.append(getGrammarHeader());
buf.append("\r\noptions {", "superClass=", parserSuperClass.getSimpleName(), ";");
if (grammarOptions != null && !grammarOptions.trim().equals("")) {
buf.append(grammarOptions);
}
buf.append("}");
buf.append(NEWLINES, lexerHeader.replace("%package%", targetPackage), NEWLINES,
"@lexer::members {", lexerMembers, additionalLexerMembers, '}', NEWLINES,
parserHeader.replace("%package%", targetPackage).replaceAll("%superclass%",
parserSuperClass.getCanonicalName()), NEWLINES, parserMembers,
NEWLINES);
for (AbstractAntlr3Rule rule : rules) {
rule.addToSerializationBuffer(buf);
buf.append(NEWLINES);
}
buf.append(lexerString);
return buf.toString();
}
/**
* Sets the fixed string.
*
* @param fixedString
* the new fixed string
*/
public void setFixedString(final String fixedString) {
this.lexerString = fixedString;
}
/**
* Adds the rule.
*
* @param rule
*/
public void addRule(AbstractAntlr3Rule rule) {
rules.add(rule);
}
/**
* Sets the grammar options, superClass is already set and cannot be set, options must be
* passed in the format "name=value;".
*
* @param grammarOptions
* the new grammar options
*/
public void setGrammarOptions(final String grammarOptions) {
// TODO allow adding of options rather than setting
this.grammarOptions = grammarOptions;
}
/**
* @param memberParts
*/
public void addLexerMembers(List<String> memberParts) {
for (String string : memberParts) {
additionalLexerMembers += "\n" + string;
}
}
public void setParserSuperClass(Class<? extends ObservableInjectingParser> parserSuperClass) {
this.parserSuperClass = parserSuperClass;
}
public void setSyntaxUUID(String uri) {
this.syntaxUUID = uri;
}
}