def _checkSystemExist(isLast=False): if not self.usecaseModel.isSystemDefined: LocalizedSourceIssue( sourceFile=self, level=Levels.Fatal, message='System is not defined %s' % (' yet.' if not isLast else '!'), line=line_no, )
def checkAllowed(self, feature, message, lineNo=None, level=Levels.Fatal): # type: (Text, Text, int, Level, Text) -> bool """ Check if a feature is allowed. If this not the case raise an issue with a proper message (see the code). """ is_allowed = feature in self.allowedFeatures if not is_allowed: if lineNo is None: Issue(self, level=level, message=message) else: LocalizedSourceIssue(sourceFile=self, level=level, message=message, line=lineNo) return is_allowed
def _parse(): for line_no in range(1, len(modelSource.sourceLines) + 1): # --------- model definition ---------------------- md = _matchModelDefinition(lineNo=line_no, modelSourceFile=modelSource, justMatch=False, noSymbolChecking=noSymbolChecking, recognizeUSEOCLNativeModelDefinition= recognizeUSEOCLNativeModelDefinition) if md is not None: if modelSource.importBox.modelName is not None: LocalizedSourceIssue( sourceFile=modelSource, line=line_no, level=Levels.Warning, message=('The model is already named : "%s"' % modelSource.model.modelName)) modelSource.importBox.setModelInfo(modelName=md.name, modelKind=md.modelKind) continue # --------- model import ---------------------- mi = _matchModelImport(lineNo=line_no, modelSourceFile=modelSource, justMatch=False) if mi is not None: if DEBUG >= 1 or Config.realtimeImportPrint >= 1: print('\nimp: >>>>>>>> ' + repr(mi)) modelSource.importBox.addImport(SourceImport(importStmt=mi)) if DEBUG >= 1 or Config.realtimeImportPrint >= 1: from modelscripts.scripts.megamodels.printer.imports import ImportBoxPrinter ImportBoxPrinter(modelSource.importBox).display() print('imp: <<<<<<<< ' + repr(mi) + '\n') continue # --------- any other line is ok ------------------- continue
def __init__(self, modelElement, level, message, locationElement=None): #type: ('ModelElement', Level, Text, 'ModelElement') -> None self.modelElement=modelElement #type: 'ModelElement' self.locationElement=( locationElement if locationElement is not None else modelElement) if DEBUG>=2: print('ISM: %s ' % self.locationElement) #type: 'ModelElement' if hasattr(self.locationElement, 'lineNo'): line_no=self.locationElement.lineNo else: line_no=None if line_no is None: if DEBUG>=1: print('ISM: Unlocated Model Issue %s' % message) issue=Issue( origin=modelElement.model, level=level, message=message) else: if DEBUG>=1: print('ISM: Localized Model Issue at %s %s' % ( line_no, message)) issue=LocalizedSourceIssue( sourceFile=self.locationElement.model.source, level=level, message=message, line=line_no, ) self.actualIssue=issue
def _matchModelDefinition( lineNo, modelSourceFile, justMatch=False, # prefixRegexp=r' *(--)? *@?', noSymbolChecking=False, recognizeUSEOCLNativeModelDefinition=False): # type: (int, ModelSourceFile, bool, bool, bool) -> Optional[Union[bool,DefinitionStatement]] """ Check if the line is a model definition statement. If justMatch just indicates if the line is recognized. In that case it returns True otherwise None. If not justMatch build an ImportStatement (if possible) Return None if this is not the case. Return a ModelDefinitionStatement if the stmt is valid. Otherwise raise a value error. Definition statements looks like this preliminary usecase model MyModel class model MyModel If 'recognizeUSEOCLNativeModelDefinition' then the line "model <NAME>" is accepted as the model definition in USE OCL files. This is a patch good enough for now for .use file. Later the .use file could be generated and the syntax improved. """ def _parseStandardSyntax(): re_stmt = (r'^ *((?P<modelKind>\w+) +)?' + r'(?P<metamodelLabel>\w+)' + r' +model' + r' +(?P<name>\w+) *$') line = modelSourceFile.realSourceLines[lineNo - 1] m = re.match(re_stmt, line, re.MULTILINE) if m is None: return None else: if m.group('modelKind') == 'import': # because overlapping regexp return None if justMatch: return True else: return { 'modelKind': m.group('modelKind'), 'metamodelLabel': m.group('metamodelLabel'), 'name': m.group('name') } def _parseUSEOCLSyntax(): re_stmt = ('^' + r' *model' + r' +(?P<name>\w+) *$') line = modelSourceFile.realSourceLines[lineNo - 1] m = re.match(re_stmt, line, re.MULTILINE) if m is None: return None if justMatch: return True else: return { 'modelKind': '', 'metamodelLabel': 'class', 'name': m.group('name') } def _parse(): r1 = _parseStandardSyntax() if r1 is not None or not recognizeUSEOCLNativeModelDefinition: return r1 else: # No match so search USEOCL return _parseUSEOCLSyntax() m = _parse() if m in [True, None]: return m else: source_metamodel = modelSourceFile.metamodel # get actual metamodel metamodel_label = m['metamodelLabel'] try: # could raise ValueError from modelscripts.megamodels import Megamodel metamodel = Megamodel.theMetamodel( label=metamodel_label) #type: Metamodel except ValueError as e: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=str(e)) # check model_kind model_kind = ('' if m['modelKind'] is None else m['modelKind']) # noinspection PyUnboundLocalVariable if model_kind not in metamodel.modelKinds: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=( '%s models can\'t be "%s".' ' Choose one of %s.' % (metamodel_label, model_kind, str(metamodel.modelKinds)))) # Check that the metamodel is the expected one # if metamodel != source_metamodel: # LocalizedSourceIssue( # sourceFile=modelSourceFile, # line=lineNo, # level=Levels.Fatal, # could be error with some work # message=( # 'A %s model cannot be defined in a "%s" file.' % ( # metamodel.label, # source_metamodel.extension, # ))) # Check name name = m['name'] if not noSymbolChecking and not (Symbol.is_CamlCase(name)): LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Error, message=('Invalid model name "%s". It must be in CamlCases.' % (name))) if name.lower() != modelSourceFile.name.lower(): if not recognizeUSEOCLNativeModelDefinition: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Error, message=( 'Model must be named %s according to the name of the file' % ( # modelSourceFile.fileName, modelSourceFile.name))) return DefinitionStatement(lineNo=lineNo, sourceFile=modelSourceFile, modelKind=model_kind, metamodel=metamodel, name=name)
def _matchModelImport(lineNo, modelSourceFile, justMatch=False): #Megamodel statements are always extracted from # RAW UNPROCESSED ORIGINAL source file. #type: (int, ModelSourceFile, bool) -> Optional[Union[bool,ImportStatement]] """ Check if the line is an import statement. If justMatch just indicates if the line is recognized. In that case it returns True otherwise None. If not justMatch build an ImportStatement (if possible) Return None if this is not the case. Otherwise return a ModelImportStatement if the import is valid. Otherwise raise a fatal error that go in the issue box. Import statements looks like this import usecase model x.cs import glossary model a/b/../c.gls """ re_stmt = (r'^ *(?P<import>import)' + r' +(?P<metamodelLabel>\w+)' + r' +model' + r' +from' + r' +\'(?P<target>[\w\./\-]+)\' *$') line = modelSourceFile.realSourceLines[lineNo - 1] m = re.match(re_stmt, line, re.MULTILINE) if m is None: return None else: if justMatch: return True else: source_metamodel = modelSourceFile.metamodel # get actual metamodel metamodel_label = m.group('metamodelLabel') try: # could raise ValueError from modelscripts.megamodels import Megamodel metamodel = Megamodel.theMetamodel( label=metamodel_label) #type: Metamodel except ValueError as e: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=str(e)) # Check that metamodel dependency is allowed target_mms = source_metamodel.outMetamodels # noinspection PyUnboundLocalVariable if metamodel not in target_mms: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=('A %s model cannot reference a %s model.' % (source_metamodel.label, metamodel.label))) # Check path literal_target_filename = m.group('target') abs_target_filename = os.path.abspath( os.path.join(modelSourceFile.directory, literal_target_filename)) file_extension = os.path.splitext(abs_target_filename)[1] if file_extension != metamodel.extension: LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=('The extension of the file must be "%s".' % (metamodel.extension))) if not os.path.isfile(abs_target_filename): LocalizedSourceIssue( sourceFile=modelSourceFile, line=lineNo, level=Levels.Fatal, # could be error with some work message=('File not found: %s' % literal_target_filename)) return ImportStatement( lineNo=lineNo, sourceFile=modelSourceFile, metamodel=metamodel, absoluteTargetFilename=abs_target_filename, literalTargetFileName=literal_target_filename)
def parseToFillModel(self): def _checkSystemExist(isLast=False): if not self.usecaseModel.isSystemDefined: LocalizedSourceIssue( sourceFile=self, level=Levels.Fatal, message='System is not defined %s' % (' yet.' if not isLast else '!'), line=line_no, ) def _ensureActor(name): _checkSystemExist(isLast=False) if name in self.usecaseModel.actorNamed: return self.usecaseModel.actorNamed[name] else: return Actor(self.usecaseModel, name) def _ensureUsecase(name): _checkSystemExist(isLast=False) if name in (self.usecaseModel.system.usecaseNamed): return self.usecaseModel.system.usecaseNamed[name] else: return Usecase(self.usecaseModel.system, name) def begin(n): return '^' + ' ' * n end = ' *$' if DEBUG >= 1: print('\nParsing %s\n' % self.fileName) current_actor = None current_usecase = None #FIXME: use doc-for in all the parser current_element = self.usecaseModel current_scope = 'model' # model | system | actor | usecase | actor.usecases for (line_index, line) in enumerate(self.sourceLines): original_line = line # replace tabs by spaces line = line.replace('\t', ' ') line_no = line_index + 1 if DEBUG >= 2: print('#%i : %s' % (line_no, original_line)) #---- blank lines --------------------------------- r = '^ *$' m = re.match(r, line) if m: continue #---- comments ------------------------------------- r = '^ *--.*$' m = re.match(r, line) if m: continue #---- description ---------------------------------- r = '^ *\|(?P<line>.*)$' m = re.match(r, line) if m: current_element.description.addNewLine( stringLine=m.group('line'), lineNo=line_no, ) continue #---- megamodel statements ------------- is_mms = isMegamodelStatement(lineNo=line_no, modelSourceFile=self) if is_mms: # megamodel statements have already been # parse so silently ignore them continue #--- system X ------------------------- r = begin(0) + r'system +(?P<name>\w+)' + end m = re.match(r, line) if m: if self.usecaseModel.isSystemDefined: LocalizedSourceIssue( sourceFile=self, level=Levels.Warning, message='System defined twice', line=line_no, ) name = m.group('name') self.usecaseModel.system.setInfo( name=name, lineNo=line_no, ) current_element = self.usecaseModel.system current_scope = 'system' continue #--- actor X -------------------------- r = (begin(0) + r'(?P<kind>(human|system))?' + ' *actor +(?P<name>\w+)' + end) m = re.match(r, line) if m: current_usecase = None name = m.group('name') current_actor = _ensureActor(name) current_actor.kind = m.group('kind'), current_actor.lineNo = line_no current_element = current_actor current_scope = 'actor' continue #--- usecase X -------------------------- r = begin(0) + r'usecase +(?P<name>\w+)' + end m = re.match(r, line) if m: current_actor = None name = m.group('name') current_usecase = _ensureUsecase(name) current_usecase.lineNo = line_no current_element = current_usecase current_scope = 'usecase' continue if current_scope == 'actor': # --- ....usecases ------------------------ r = begin(1) + r'usecases' + end m = re.match(r, line) if m: current_scope = 'actor.usecases' continue if current_scope == 'actor.usecases': r = begin(2) + r'(?P<name>\w+)' + end m = re.match(r, line) if m: uc = _ensureUsecase(m.group('name')) current_actor.addUsecase(uc) current_element = uc uc.lineNo = line_no continue LocalizedSourceIssue(sourceFile=self, level=Levels.Error, message=('Syntax error. Line ignored.'), line=line_no) # End of file line_no = len(self.sourceLines) _checkSystemExist(isLast=True)
def _parse_main_body(self): """ Parse everything in the glossary except the description of entries. These descriptions are parsed by the subparser for TextBlock. In this first phase we just store the information as lines (and first line number). This info will be used in second phase to feed the embedded parser. """ current_context=self.glossaryModel #type: Optional[Union[GlossaryModel, Domain, Entry]] for (line_index, line) in enumerate(self.sourceLines): original_line = line line = line.replace(u'\t',u' ') line_no = line_index+1 #----------------------------------------------- # comments #----------------------------------------------- r = '^ *--.*$' m=re.match(r, line) if m: continue #----------------------------------------------- # blank lines #----------------------------------------------- r = '^ *$' m=re.match(r, line) if m: continue #----------------------------------------------- # megamodel statements #----------------------------------------------- is_mms = isMegamodelStatement( lineNo=line_no, modelSourceFile=self) if is_mms: # megamodel statements have already been # parse so silently ignore them continue #----------------------------------------------- # domain #----------------------------------------------- if (isinstance(current_context, ( GlossaryModel, Domain, Entry))): r=r'^domain +(?P<name>\w+) *$' m=re.match(r, line) if m: name=m.group('name') if name in self.glossaryModel.domainNamed: # If the domain already exists, reuse it current_content=( self.glossaryModel.domainNamed[name]) else: # Creates a new domain with this name domain=Domain( glossaryModel=self.glossaryModel, name=name, lineNo=line_no, ) current_context=domain continue #----------------------------------------------- # entry #----------------------------------------------- if isinstance(current_context, ( Domain, Entry)): r=r'^ (?P<word1>\w+)(?P<words> \w+[\w ]+)? *: *$' m = re.match(r, line, re.UNICODE) if m: if m.group('words') is None: words=[] else: words=[ w for w in m.group('words').split(' ') if w!=''] domain=( current_context.domain if isinstance(current_context, Entry) else current_context) name=m.group('word1') if name in domain.entryNamed: # The simplest way deal with the # multiple definition is to concatenate # text after existing definition. This # avoid skipping the next lines. LocalizedSourceIssue( sourceFile=self, level=Levels.Error, message= '%s.%s already exist.' ' Definitions will be appended.' % (domain.name, name), line=line_no ) # do not create an new entry # reuse instead the old entry entry=domain.entryNamed[name] else: # Create a new entry entry=Entry( domain=domain, mainTerm=name, alternativeTerms=words, lineNo=line_no, ) # Set the glossary of the description entry.description.glossary=self.glossaryModel current_context=entry continue #----------------------------------------------- # description #----------------------------------------------- if isinstance(current_context, Entry): r='^ *\|(?P<line>.*)' m = re.match(r, line) if m: entry=current_context entry.description.addNewLine( stringLine=m.group('line'), lineNo=line_no) # .descriptionBlockSourcePerEntry[entry] \ # .addTextLine( # textLine=m.group('line'), # lineNo=line_no) continue #----------------------------------------------- # error #----------------------------------------------- LocalizedSourceIssue( sourceFile=self, level=Levels.Error, message='Syntax error', line=line_no )