Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 521 lines (452 sloc) 15.7 KB
/*
Copyright: All contributers to the Umple Project
This file is made available subject to the open source license found at:
http://umple.org/license
This file contains the implementation details for our internal Umple parser.
This class parses an Umple file into an abstract syntax tree of tokens, which are then
analyzed into an instance of of Umple meta model. This meta model instance can then
be passed to our code generator to generate target implementations such as Java, PHP
and Ruby. It is configured to mostly delegate to helper methods for the following components
+ Core (most things outside of the elements below)
+ Umple Classes
+ State Machinesx
+ Composite Structure
+ Traces
+ Layouts
+ Template
The grammar is parsed from the *.grammar files in the /src directory. Please refer to the
addRulesInFile() methods to see which files comprise the current grammar.
This file also contains helper methods for delegating the analysis of token, so that
we properly populate the Umple instance of the Umple meta-model.
Please refer to UmpleInternalParser.ump for more details.
*/
namespace cruise.umple.compiler;
class UmpleInternalParser
{
internal String[] unparsedUmpleFiles;
internal String[] parsedUmpleFiles;
internal Boolean shouldProcessAgain = false;
internal Boolean shouldProcessClassAgain = false;
after constructor { init(); }
public UmpleInternalParser()
{
this("UmpleInternalParser", new UmpleModel(null), new RuleBasedParser());
}
public UmpleInternalParser(UmpleModel aModel)
{
this("UmpleInternalParser", aModel, new RuleBasedParser());
}
private void init()
{
if(model.getUmpleFile() != null)
{
setFilename(model.getUmpleFile().getFileName());
setRootToken(reset()); // Makes sure the root token position has the filename
}
Couple quotes = new Couple("\"","\"");
Couple braces = new Couple("{","}");
addCouple(quotes);
braces.addIgnore(quotes);
braces.addIgnore(new Couple("//","\n"));
addCouple(braces);
// Set up needed parser actions
parser.addParserAction("useStatement", new UseStatementParserAction() );
parser.setLinkedFileHandler( new UmpleLinkedFileHandler() );
parser.setAnalyzerGenerator( new UmpleAnalyzerGeneratorHandler() );
parser.addGrammarFile("/umple_core.grammar");
parser.addGrammarFile("/umple_classes.grammar");
parser.addGrammarFile("/umple_traits.grammar");
parser.addGrammarFile("/umple_fixml.grammar");
parser.addGrammarFile("/umple_patterns.grammar");
parser.addGrammarFile("/umple_state_machines.grammar");
parser.addGrammarFile("/umple_traces.grammar");
parser.addGrammarFile("/umple_constraints.grammar"); // TODO Under development
parser.addGrammarFile("/umple_structure.grammar");
parser.addGrammarFile("/umple_template.grammar");
parser.addGrammarFile("/umple_layout.grammar");
parser.addGrammarFile("/umple_exceptions.grammar");
parser.addGrammarFile("/use.grammar");
parser.addGrammarFile("/umple_filter.grammar"); // TODO Under development
parser.addGrammarFile("/umple_mixsets.grammar"); // Under development
}
public ParseResult parse(String ruleName, String input){
setParseResult(parser.parse(ruleName,input));
setRootToken(parser.getRootToken());
SampleFileWriter.destroy("temp.ump");
return getParseResult();
}
public ParseResult parse(String ruleName, String input, String fileName, Position position, int lineNumber, int offset){
setParseResult(parser.parse(ruleName,input,fileName, position,lineNumber,offset));
setRootToken(parser.getRootToken());
return getParseResult();
}
public ParseResult analyze(boolean shouldGenerate)
{
try {
setParseResult(parser.getParseResult());
setRootToken(parser.getRootToken());
analyzeAllTokens(getRootToken());
postTokenAnalysis();
secondPostTokenAnalysis();
//add analysis here!!! xx.validateStateMachineGuardConstraints(model);
checkDefaultedNameConflict();
}
catch (Exception ex)
{
setFailedPosition(new Position("",0,0,0),9100,
"Could not analyze Umple model. Please report an issue with this entire message and your input code to Umple developers.\nStack Trace Follows.\n"+
cruise.umple.util.ExceptionDumper.dumpCompilerError(ex));
}
if (shouldGenerate && getParseResult().getWasSuccess())
{
String generatorError = model.generate();
if(generatorError != null) {
setFailedPosition(new Position("",0,0,0),9200,generatorError);
}
}
return getParseResult();
}
boolean addToGeneratedMethodMap(HashSet<String> generatedMethods, String className, CodeTranslator t, String translatingLabel, Attribute aAttr){
String generatedMethodName = t.translate(translatingLabel, aAttr);
if(generatedMethodName != null && !"".equals(generatedMethodName) && !generatedMethodName.startsWith("UNKNOWN ID")){
boolean success = generatedMethods.add(generatedMethodName);
if(!success){
getParseResult().addErrorMessage(new ErrorMessage(48,aAttr.getPosition(), aAttr.getName(), className, generatedMethodName));
return false;
}
}
return true;
}
void checkDefaultedNameConflict(){
Map<String, CodeTranslator> allTranslators = model.getAllTranslators();
HashSet<String> generatedMethods = new HashSet<String>();
for(String langName : allTranslators.keySet()){
CodeTranslator t = allTranslators.get(langName);
for(UmpleClass aClass : model.getUmpleClasses()){
generatedMethods.clear();
for(Attribute aAttr : aClass.getAttributes()){
String modifier = aAttr.getModifier();
// Can add more!
if(!"internal".equals(modifier)){
addToGeneratedMethodMap(generatedMethods, aClass.getName(), t, "getMethod", aAttr);
addToGeneratedMethodMap(generatedMethods, aClass.getName(), t, "setMethod", aAttr);
}
if("defaulted".equals(modifier)){
addToGeneratedMethodMap(generatedMethods, aClass.getName(), t, "getDefaultMethod", aAttr);
}
}
}
}
}
//------------------------
// PRIVATE METHODS
//------------------------
// When an error occurs, set the failed position and mark the compile as NOT successful
public void setFailedPosition(Position position, int errorCode, String... messages)
{
//getParseResult().setWasSuccess(false);
// Handle the special case of multiple unrecognized lines in a class; ensures that the
// 1007 warning message is recorded and displayed no more than once
if (errorCode == 1007) {
List<ErrorMessage> errorMessages = getParseResult().getErrorMessages();
for (ErrorMessage error : errorMessages) {
if (error.getErrorType().getErrorCode() == 1007) {
return; // we have already recorded this error, lets ignore and return
}
}
}
getParseResult().setPosition(position);
getParseResult().addErrorMessage(new ErrorMessage(errorCode,position,messages));
}
// Analyze all child tokens of the "root" token. This delegates to a individual
// Each token is analyzed as long as "shouldProcessAgain" is set to true during the analysis
// analyzeToken and quits early if a problem arises
private void analyzeAllTokens(Token rootToken)
{
int analysisStep = 0;
shouldProcessAgain = true;
do
{
analysisStep += 1;
shouldProcessAgain = false;
for(Token t : rootToken.getSubTokens())
{
analyzeToken(t,analysisStep);
if (!getParseResult().getWasSuccess())
{
return;
}
}
}
while (shouldProcessAgain);
}
// Delegate function to analyze a token within the context of a class
// Each token is analyzed as long as "shouldProcessClassAgain" is set to true during the analysis
// "1" is for the first round of analysis and "2" for the second. The "2" is used for chicken-and-egg initialization problems, otherwise
// put everything under the "1"
private void analyzeAllTokens(Token rootToken, UmpleClass aClass)
{
int analysisStep = 0;
shouldProcessClassAgain = true;
do
{
analysisStep += 1;
shouldProcessClassAgain = false;
for(Token token : rootToken.getSubTokens())
{
analyzeToken(token,aClass,analysisStep);
if (!getParseResult().getWasSuccess())
{
return;
}
}
}
while (shouldProcessClassAgain);
}
// Delegate function to analyze a token and send it to the write
private void analyzeToken(Token t, int analysisStep)
{
analyzeMixsetUseStatement(t, analysisStep);
analyzeUSE(t, analysisStep);
analyzeCoreToken(t,analysisStep);
analyzeEnumerationToken(t, analysisStep);
analyzeClassToken(t,analysisStep);
analyzeTraitToken(t,analysisStep);
analyzeTemplateToken(t,analysisStep);
analyzeStateMachineToken(t,analysisStep);
analyzeComponentToken(t,analysisStep);
analyzeTraceToken(t,analysisStep);
analyzeLayoutToken(t,analysisStep);
analyzeFilterToken(t,analysisStep);
analyzeFIXML(t, analysisStep);
analyzeMixsetToken(t, analysisStep);
}
// Analyze an individual token, delegates to the various components in Umple
private void analyzeToken(Token t, UmpleClass aClass, int analysisStep)
{
analyzeCoreToken(t,aClass,analysisStep);
analyzeClassToken(t,aClass,analysisStep);
analyzeStateMachineToken(t,aClass,analysisStep);
analyzeDependentTokens(t,aClass,analysisStep);
}
//The analysis methods in this function can depend on prior analysis (ex: constraints/keys on attributes defined later in the code)
//Therefore it is necessary to force a second pass of the analysis for these cases
private void analyzeDependentTokens(Token t, UmpleClass aClass, int analysisStep)
{
// Methods after this point will occur on analysisStep > 1
if (analysisStep <=2 && t.is("invariant"))
{
shouldProcessAgain = true;
}
if(analysisStep <= 2 || shouldProcessClassAgain)
{
shouldProcessClassAgain = true;
return;
}
if (t.is("modelConstraintBody"))
{
analyzeModelConstraint(t,aClass);
}
else if (t.is("beforeCode") || t.is("afterCode"))
{
analyzeInjectionCode(t,aClass);
}
else if (t.is("key") || t.is("defaultKey"))
{
addKey(t, aClass, unlinkedKeysTokens);
}
analyzeTraceToken(t,aClass);
analyzeLayoutToken(t,aClass,analysisStep);
}
// Once you have analyze all tokens, you allowed a second 'pass' to apply any additional checks
// Each step in the process might "fail", so we check the status before calling each delegate
// token post token analysis method
private void postTokenAnalysis()
{
analyzeParseResult();
if (getParseResult().getWasSuccess())
{
postTokenCoreAnalysis();
}
if (getParseResult().getWasSuccess())
{
postTokenInterfaceAnalysis();
}
if (getParseResult().getWasSuccess())
{
postTokenClassAnalysis();
}
if (getParseResult().getWasSuccess())
{
postTokenTraceAnalysis();
}
if (getParseResult().getWasSuccess())
{
postTokenTraitAnalysis();
}
if (getParseResult().getWasSuccess())
{
applyTraits();
}
if (getParseResult().getWasSuccess())
{
postTokenLayoutAnalysis();
}
if (getParseResult().getWasSuccess()) {
postTokenTemplateAnalysis();
}
if (getParseResult().getWasSuccess()) {
postTokenComponentAnalysis();
}
if (getParseResult().getWasSuccess())
{
postTokenModelConstraintAnalysis();
}
if (getParseResult().getWasSuccess())
{
postTokenEnumerationAnalysis();
}
}
//This is used for the cases that will be resolved by traits and we don't need to see those warnings.
private void secondPostTokenAnalysis() {
if (getParseResult().getWasSuccess())
{
postTokenStateMachineAnalysis();
}
if (getParseResult().getWasSuccess()){
//Issue 492
checkNonReachableStates();
}
}
private void postTokenModelConstraintAnalysis(){
for(UmpleClass uClass:model.getUmpleClasses())
{
for(ModelConstraint modelConstraint:uClass.getModelConstraints())
{
ModelConstraintResult result = modelConstraint.evaluate(uClass);
if(result!=ModelConstraint.SUCCESS)
{
setFailedPosition(result.getPosition(),result.getError(),result.getTarget(),result.getSource());
}
}
}
}
// Locate all 'use *.ump' references and add those files if not already parsed
private void addNecessaryFiles()
{
}
// Loop through all unparsed files, parse them, and add any missing references
private void parseAllFiles()
{
addNecessaryFiles();
while (!unparsedUmpleFiles.isEmpty() && getParseResult().getWasSuccess())
{
String nextFile = unparsedUmpleFiles.get(0);
unparsedUmpleFiles.remove(0);
parsedUmpleFiles.add(nextFile);
String input = SampleFileWriter.readContent(new File(nextFile));
//TODO: parse() should probably be responsible for
// reading file data, if we refactor it to be as such,
// then we can get rid of this ugly bit of code
setFilename(nextFile);
parse("program", input);
addNecessaryFiles();
}
}
public void analyzeParseResult()
{
int numberOfErrors = 0;
int numberOfWarnings = 0;
for(int i = 0; i<getParseResult().numberOfErrorMessages(); i++)
{
ErrorMessage error = getParseResult().getErrorMessage(i);
if(error.getErrorType().getSeverity() <= 2)
{
numberOfErrors++;
}
else
{
numberOfWarnings++;
}
if(allowedMessages.contains(error.getErrorType().getErrorCode()))
{
if(error.getErrorType().getSeverity() <= 2)
{
numberOfErrors--;
}
else
{
numberOfWarnings--;
}
getParseResult().removeErrorMessage(error);
i--;
continue;
}
if(ignoredMessages.contains(error.getErrorType().getErrorCode()))
{
if(error.getErrorType().getSeverity() <= 2)
{
numberOfErrors--;
}
else
{
numberOfWarnings--;
}
getParseResult().removeErrorMessage(error);
i--;
continue;
}
if(expectedMessages.contains(error.getErrorType().getErrorCode()))
{
if(error.getErrorType().getSeverity() <= 2)
{
numberOfErrors--;
}
else
{
numberOfWarnings--;
}
}
}
for(Integer id: expectedMessages)
{
boolean hasMessage = false;
for(int i = 0; i<getParseResult().numberOfErrorMessages(); i++)
{
ErrorMessage error = getParseResult().getErrorMessage(i);
if(error.getErrorType().getErrorCode() == id)
{
getParseResult().removeErrorMessage(error);
hasMessage = true;
break;
}
}
if(!hasMessage)
{
// setFailedPosition
}
}
if(numberOfErrors == 0)
{
getParseResult().setWasSuccess(true);
}
if(numberOfWarnings == 0)
{
getParseResult().setHasWarnings(false);
}
}
}
use UmpleInternalParser_CodeCore.ump;
use UmpleInternalParser_CodeClass.ump;
use UmpleInternalParser_CodeTrait.ump;
use UmpleInternalParser_CodeConstraints.ump;
use UmpleInternalParser_CodeStructure.ump;
use UmpleInternalParser_CodeTemplate.ump;
use UmpleInternalParser_CodeStateMachine.ump;
use UmpleInternalParser_CodeTrace.ump;
use UmpleInternalParser_CodeLayout.ump;
use UmpleInternalParser_CodeUSE.ump;
use UmpleInternalParser_CodeFilter.ump;
use UmpleInternalParser_CodeParserHandlers.ump;
use UmpleInternalParser_CodeEnumeration.ump;
use UmpleInternalParser_CodeMixset.ump;